Authentication
This content is not available in your language yet.
Every request to the Data API carries one header:
Authorization: Bearer sk_live_7Hq2mNp9xR4tK8wL3vB6yC1dF5gJ0aZsThere are no sessions, cookies or OAuth flows. A key identifies the client organisation, its plan, its environment and the scopes it may use.
Key format
Section titled “Key format”sk_{env}_{random} env = test | live, random = 32 base62 characters| Prefix | Environment | Sees | Billed |
|---|---|---|---|
sk_test_ |
sandbox | the 20 shared demo parcels plus up to 10 of your own | never |
sk_live_ |
production | your parcels | per plan |
Sandbox and live share the same base URL; the prefix decides which world a
request lands in. A live key cannot read sandbox parcels and a test key cannot
read live ones. Every parcel carries environment: "test" or "live".
Where keys come from
Section titled “Where keys come from”Keys are issued in the client portal. A key is shown once, at creation; we store only its SHA-256 hash and a 12 character prefix for display, so a lost key cannot be recovered - rotate it instead.
Check what a key can do at any time with a call that needs no other input:
curl -s "$BASE/account" -H "Authorization: Bearer $KEY"The answer lists the plan, the products it includes, the scopes of this
key, the parcel quota and the rate limits. This endpoint keeps answering while
an account is suspended for an overdue invoice; every other endpoint then
returns 402 payment_required.
Scopes
Section titled “Scopes”Each endpoint requires one scope. The scopes of the sandbox key are the full set:
parcels:read parcels:write indices:read sar:read thermal:readyield:read risk:read damage:write damage:read index:writecap:read webhooks:manageA key cannot carry a scope that its plan does not include. Two different errors tell you which of the two is missing:
403 insufficient_scope- the plan includes the product, the key was issued without that scope.detail.required_scopenames it; issue a key with the scope in the portal.403 plan_does_not_include- the product is outside the plan.detail.productnames it; upgrade the plan or drop the call.
Rotation without downtime
Section titled “Rotation without downtime”A client may hold two active live keys at the same time. Create the new
key, deploy it, then revoke the old one. Revoked keys answer 401 key_revoked
from the moment of revocation; keys with an expiry date answer
401 key_expired after it. Both are final - use a current key.
Key lookups are cached for 60 seconds, so a revocation can take up to a minute to be enforced on every node.
IP allowlist
Section titled “IP allowlist”Optional, recommended for enterprise deployments: restrict a key to a list of
egress IPs or CIDRs in the portal. A call from anywhere else answers
403 ip_not_allowed with detail.ip, the address our edge saw on the TCP
connection. X-Forwarded-For and X-Real-IP sent by the caller are ignored -
they cannot be used to step around the list - so the address to allow is the
public egress address of your servers, not one you put in a header.
Errors you can get before any business logic runs
Section titled “Errors you can get before any business logic runs”error.type |
HTTP | Meaning |
|---|---|---|
authentication_failed |
401 | Missing, malformed or unknown key. Check the header name and the Bearer prefix. |
key_revoked |
401 | Revoked in the portal. |
key_expired |
401 | Past its expiry date. |
payment_required |
402 | Account suspended (14 days overdue) or closed (45 days); detail.status says which. GET /v1/account still works while suspended. |
insufficient_scope |
403 | Key lacks the scope; detail.required_scope. |
plan_does_not_include |
403 | Product outside the plan; detail.product. |
ip_not_allowed |
403 | Caller IP not on the key allowlist; detail.ip. |
All of them use the standard error envelope. Authentication runs
first: a request without a valid key gets 401 even when its body is not
valid JSON, and a call counts against your rate limit only after the key was
accepted.
Transport
Section titled “Transport”The API is HTTPS only; every /v1 response carries
X-Content-Type-Options: nosniff, and the production host adds
Strict-Transport-Security. /v1 answers CORS preflights from any origin
without credentials and exposes the headers an integration needs
(X-Request-Id, the X-RateLimit-* family, Retry-After,
Idempotent-Replay, X-Agrovisio-Not-Billable, X-Agrovisio-Warning,
X-Data-Attribution, Content-Disposition, Deprecation, Sunset, Link)
to fetch(); allowed request headers are Authorization, Content-Type,
Accept, Idempotency-Key and X-Request-Id. That makes a browser call
possible, not advisable - see the next section.
Keep the key out of the browser
Section titled “Keep the key out of the browser”The interactive reference stores the key you paste in your own browser’s local storage so you do not retype it. That is fine for a sandbox key. Never paste a live key into a page served from a host you do not control, and never ship a key in client-side code: the Data API is meant to be called from your backend.
