ThemeFontsMountReady
SEND
DocsPricingFeaturesComparisonsHelpSign inRequest access →

SEND / Docs

API Authentication

Bearer tokens, their real lifetimes, how to refresh them, and the one action that revokes them.

← DocsAPI Reference5 min readUpdated August 10, 2026

Authentication Overview

SEND authenticates with bearer tokens, not with cookies. Signing in returns a JWT that you attach to every subsequent request as an Authorization: Bearer header. No cookie is ever set and nothing on the API depends on one, so a client that only manages cookies will be treated as signed out on every call.

Two kinds of credential travel in that same header, and the server tells them apart by prefix. Anything beginning with ck_ is validated as an API key; anything else is validated as a JWT access token.

The SEND web app handles all of this for you. This guide is for developers building their own integration.

Logging In

Post your credentials to the login route. On success the response body carries the tokens; storing them and sending them back is entirely the client's job.

  1. POST /api/v1/auth/login with { "username": "...", "password": "..." }
  2. On success: 200 with { user, accessToken, refreshToken, tokenType, expiresIn }. expiresIn is 1800 -- the access token's lifetime in seconds
  3. Send accessToken on every following request as Authorization: Bearer <accessToken>
  4. On failure: 401. Every credential failure reads "invalid credentials", whether or not the username exists
  5. With two-factor enabled the response carries requires2FA and a challengeToken instead of tokens -- finish the sign-in by presenting the code with that challenge

Token Lifetimes

Two tokens, two fixed lifetimes, both set at the moment of issue. Expiry is absolute: nothing extends a token because you are still making requests, and nothing ends it early because you paused.

There is no session list, no per-device record, and no per-session revoke. POST /api/v1/auth/logout answers with a success message and revokes nothing -- there is no token blacklist, so a copied token keeps working until it expires. The one action that invalidates already-issued tokens is changing the account password, which advances the account's session version and kills every access and refresh token, on every device, at once.
  • Access token: 30 minutes, reported as expiresIn: 1800 on the sign-in response
  • Refresh token: 30 days
  • Both lifetimes are fixed in the build -- there is no per-account, per-device or per-key setting
  • A valid signature is not sufficient on its own: every authenticated request reloads the account, so a deactivated account stops working on its very next request rather than when its token runs out

Refreshing an Access Token

When the 30 minutes are up, exchange the refresh token for a new pair rather than signing in again.

  • POST /api/v1/auth/refresh with { "refreshToken": "..." }. The response carries a fresh accessToken and refreshToken
  • Every refresh issues a new refresh token, so a client in continuous use rolls forward indefinitely and a client left idle stops working 30 days after its most recent one
  • A failed refresh is terminal -- discard both tokens and sign in again
  • The SEND web client does this silently: a 401 triggers one refresh and then replays the original request, and several requests failing at once queue behind that single refresh instead of each firing their own

Error Handling

Authentication errors return standard HTTP status codes. Handle these in your integration to provide a good user experience.

Treat both tokens as secrets. The web app keeps them in browser local storage, which means any JavaScript running on the page can read them -- never paste third-party scripts into the console, and never put a token in a URL, a log line, or a bug report. In your own integration keep them in memory or in your platform's secret store. An API key is worse to leak, not better: it authenticates as your entire account and the permission labels recorded at creation are not enforced as a scope, so there is no read-only key. Revoke one from Settings > API Keys the moment it is no longer in use.
  • 401 Unauthorized -- the access token is missing, expired, or no longer matches the account. Refresh once; if the refresh fails too, sign in again
  • 403 Forbidden -- authenticated, but not permitted to touch that resource
  • 429 Too Many Requests -- rate limit exceeded. Reads are 120 per minute, writes 60, heavy writes 5. Back off using the Retry-After header
←Previous
Creating Wallets
→Next
Wallets API
Was this article helpful?

SEND

Execution infrastructure for Solana. Coordinated execution across thousands of wallets, with a footprint that reads as thousands of strangers.

TwitterDiscordGitHub

Product

FeaturesPricingComparisonsChangelogDocumentation

Company

AboutAmbassador ProgramContact

Legal

PrivacyTerms

Support

Help CenterDiscordTwitter

© SEND 2026 — All rights reserved

Powered by ChainKit