Developers
Start building
APIs/Access errors

Access errors

What an API token or OAuth grant is refused with, and what to do about it.

On this page

A call made with an API token (pltu_, plto_, pltd_) or an OAuth access token is decided by Poltura's gateway against the grant behind it. A refusal always has the same JSON body:

{
  "error": "permission_denied",
  "message": "this token does not allow orders.write on store:0199f3a2-7c41-7d2e-9b1a-3c5e8f0a1b2c",
  "permission": "orders.write",
  "resource": "store:0199f3a2-7c41-7d2e-9b1a-3c5e8f0a1b2c",
  "documentation_url": "https://developers.poltura.com/api/errors#permission-denied"
}

permission and resource appear when the refusal is about one permission on one store or organisation.

invalid-token

401. The credential is unknown, malformed, expired, revoked, disabled or exposed. The response carries WWW-Authenticate: Bearer error="invalid_token", resource_metadata="…"; an OAuth client starts a new authorization there, a token holder creates or rolls the token.

invalid-dpop-proof

401. The credential is a Poltura CLI sign-in, which is bound to a key (DPoP), and the request did not prove it holds that key. The proof is missing, signed by another key, made for another method, URL or token, too old, or already used. The token was sent as Bearer instead of DPoP, or another credential was sent with the DPoP scheme. The response carries WWW-Authenticate: DPoP error="invalid_dpop_proof". Use the CLI on the machine that signed in, or run poltura login again.

use-dpop-nonce

401 (400 from the token endpoint). The proof's time is too far from the server's. The response carries a DPoP-Nonce header: put that value in the proof's nonce claim and send the request again. The CLI does this itself.

insufficient-scope

403. An OAuth grant lacks the permission, and asking for it again can help. The challenge lists every permission the grant already holds plus the missing one:

WWW-Authenticate: Bearer error="insufficient_scope", scope="orders.read products.read orders.write",
  resource_metadata="…"

Re-authorize with exactly that scope. A grant that already holds the permission is never challenged again.

permission-denied

403. The token's policy does not allow the permission, the grant does not reach this store, or the person who owns the access no longer holds the permission in their role. No new consent changes that.

not-delegable

403. The permission can never be used through a credential: it needs a second factor, belongs to an owner only, manages access itself, or moves money. Do it in the admin.

ip-not-allowed

403. The token is limited to addresses this call did not come from.

access-disabled

403. The organisation switched off third-party apps and agents, the store switched off AI agents for /mcp, the client was blocked, or the store or the grant is suspended.

approval-required

409. An agent action is waiting for a person to approve it. See Approval.

request-too-costly

400. One call would cost more than 100 points, for example a list with a very large limit and many includes. It was not run. Ask for a smaller page or fewer includes. The costs are on How agents act on your store.

rate-limited

429. The points budget of this credential, of this client at this store, or of the store as a whole is used up. The body names the bucket that ran out and retryAfterMs, and the response carries Retry-After in seconds and the X-Poltura-RateLimit-* headers. The call did not reach the store, so waiting that long and sending it again is safe. Inside an MCP tool call the same refusal is a tool error with the code rate_limited.

unavailable

503. Poltura could not decide the call just now. Retry after Retry-After seconds; nothing was done.

Last updated