OnLink
Concepts

Products and scopes

Why an endpoint documented on this site answers 403 for your credential: every key carries an explicit list of what it may call, and there is no wildcard.

The reference documents every operation this API has. Your credential reaches the subset your agreement covers. When those two differ you get a 403, and this page is how to read it.

A credential carries an explicit list

Every API key is issued with a list of scopes in resource:action form. A call to an operation whose scope is not on your list is refused — by the credential, before the operation runs.

Three properties of that list are worth knowing before you debug one:

  • There is no wildcard. No *, no admin, no super-scope. The list is exhaustive.
  • An empty list authorises nothing. It is not a shorthand for "unrestricted".
  • Widening a credential means issuing a new one. A scope cannot be added to a key in place, which is also why the grant is worth getting right at issuance rather than discovering in integration.

The scopes

ScopeWhat it unlocks
health:readGET /v1/health — the smallest signed call, for testing signing.
quotes:writeTaking a quote.
orders:writeCreating an order, and attaching a transaction hash to one.
orders:readReading an order back.
balances:readThe account you hold with us, and what is available in it.
funding:readYour deposit instructions and collection account.
payout_accounts:readThe Kenyan bank accounts a payout can settle to.
wallets:writeRegistering, confirming and removing a withdrawal address.
wallets:readListing your registered addresses.
admins:writeRegistering and removing an approver on your account.
admins:readListing them.

A read scope never implies its write counterpart, and a write scope never implies the read. Ask for both where you need both.

What a refusal looks like

Response — 403 Forbidden:

{
  "statusCode": 403,
  "message": "This credential is not authorised for this endpoint. Required scope: orders:write.",
  "error": "insufficient_scope",
  "requestId": "req-1756512000000-a1b2c3d",
  "errorCode": "orders:write"
}

error is the constant insufficient_scope. The scope the route needed is in errorCode, and message repeats it in prose — so a refusal tells you what to ask for rather than sending you to us to find out.

What the response does not contain is the list your credential holds. That is a description of your own privileges, and it is more than a single refusal needs to disclose.

A 403 is not a signing problem

It is a 403 and not a 401 deliberately: your signature, your key and your clock were all correct, and the call was authenticated. Rotating a working secret will not fix it. A 401 is the signing path — see Authentication.

Why your grant is shaped the way it is

A grant is scoped to what your integration does. A platform that only converts in one direction does not need the other direction's write scope, and a credential that never registers a destination does not need wallets:write. That is not friction for its own sake: the narrower the grant, the less a leaked secret can do, and the destinations it could reach are registered in advance anyway. See API credentials.

If you need a scope you were not issued, tell us what you are building and we will issue a credential that carries it. Email info@onlink.africa.

On this page