Skip to content

Authentication

Every request to the Data API carries one header:

Authorization: Bearer sk_live_7Hq2mNp9xR4tK8wL3vB6yC1dF5gJ0aZs

There are no sessions, cookies or OAuth flows. A key identifies the client organisation, its plan, its environment and the scopes it may use.

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".

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:

Terminal window
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.

Each endpoint requires one scope. The scopes of the sandbox key are the full set:

parcels:read parcels:write indices:read sar:read thermal:read
yield:read risk:read damage:write damage:read index:write
cap:read webhooks:manage

A 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_scope names it; issue a key with the scope in the portal.
  • 403 plan_does_not_include - the product is outside the plan. detail.product names it; upgrade the plan or drop the call.

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.

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.

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.

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.