Skip to main content
Every Partner API request is authenticated with a user access token issued through the OAuth flow. This page covers what the credentials are, how long they live, and how to renew them without breaking a connection.

Credential Prefixes

Every Trench credential carries a prefix, so you can assert on the shape of a value before storing it.
The Authorization header is checked against the trench_at_ prefix before any database lookup happens. Sending a refresh token where an access token belongs therefore fails as 401 invalid_token rather than as an expiry or scope error.

Lifetimes

Because each refresh issues a new 60-day token, an actively used connection never lapses, and your users are not asked to reconnect on a schedule.

Storing Tokens

Store the pair against your own user record.
Trench stores only hashes of these values, so support cannot read a token back to you.

Exchanging the Code

Exchange the code from your backend, authenticating with HTTP Basic. Your API key should never reach a browser.
Response
Credentials can also be sent as client_id and client_secret form fields if Basic auth is awkward in your HTTP client. Responses are returned with cache-control: no-store. The redirect_uri must match the one stored with the code, and the code_verifier must hash to the code_challenge you sent. Only S256 is accepted, and the verifier must be 43 to 128 characters of [A-Za-z0-9_-].
Authorization codes are single use. Presenting one a second time revokes every token that code issued and returns invalid_grant.

Refreshing

cURL
Refresh tokens rotate on every use. Each refresh returns a new pair and marks the old refresh token as replaced, so both new values need to be persisted. Three practices follow from rotation:
  1. Refresh lazily, on a 401 or when your stored expiry has passed, rather than on a timer.
  2. Never refresh concurrently for the same user. Two refreshes of the same token at the same instant count as reuse, so one wins and the connection is revoked. If several of your workers can refresh the same user, take a lock or funnel refreshes through one place. The same applies to exchanging an authorization code, which should happen once, from one process.
  3. Never retry a 401 in a loop. Refresh once, and if that fails, send the user back through authorization.
Reusing a rotated-out refresh token is treated as a sign of compromise and revokes the user’s entire grant, including every access and refresh token issued under it rather than only the one you replayed. The user will need to reconnect from scratch.
Only the refresh token rotates. The previous access token is not revoked and remains valid until its own 30-minute expiry, so for a short window both tokens work. This is convenient for requests already in flight during a refresh, but it does mean refreshing is not a way to cut off a leaked access token. To do that, revoke it explicitly.

Tokens and Your API Key

Every token records the API key that minted it. A refresh rebinds the new pair to whichever key you present, so an app that has rotated ends up with tokens tied to the key it still holds rather than the original.
Revoking an API key revokes every live token it minted, both access and refresh, and those users have to reconnect from scratch. This is deliberate, since it is what makes revocation a real kill switch for a leaked key rather than a 60-day wait. See Rotating a Key for the sequence that avoids disconnecting anyone.

Revoking

cURL
What gets revoked depends on which token you send, and the difference is significant. To disconnect a user properly, send the refresh token. Sending the access token only forces an early refresh. Revocation returns 200 with an empty body in either case, including for tokens that were already dead. Users can also disconnect your app from their Trench settings, Trench can suspend an app, and revoking an API key ends every connection made through it. All three take effect on the next request and surface as 401 invalid_token.
Every authentication failure returns the same 401 invalid_token with no distinguishing detail, so expired, revoked, malformed, unknown, and suspended-client cases all look identical from the outside. Since you cannot branch on the reason, we recommend refreshing once and treating a second failure as a lost connection.

Checking the Connection

cURL
Both walletSolana and twitterHandle may be null. The scopes array reflects what the token can actually do, which may differ from what you requested at authorize time, so we recommend treating it as the source of truth.
A value of tradingEnabled: false means the user has not delegated their wallet for 1-click trading. Trades and governance writes will fail with delegation_missing until they do, so send them back through the authorize flow.