Skip to main content
Every call to the StableMint API is signed. There are no bearer tokens, no OAuth client-credentials flow, and no plain API-key header. You send four headers and an RSA signature computed over a canonical string.
Never send an Authorization header on a signed request. Any Bearer token is rejected with 401 jwt_not_accepted before the request reaches a handler — whether or not the token is valid, and whether or not you also sent correct signature headers. The token is never parsed; its mere presence is the rejection. If your HTTP client sets an Authorization header globally, remove it for StableMint calls.

The four headers

All four are required. Sending some but not all returns 401 with code: signature_required. Sending a dashboard session token returns 401 with code: jwt_not_accepted.
Some operations additionally require a permission on your service account. A valid signature without the required permission returns 403, not 401 — if you are getting 403, your signing is fine and the fix is a permission grant, not a code change. See Service accounts.

The canonical string

Seven fields, joined with a single line feed (\n, U+000A), in exactly this order. No trailing newline.
string
Uppercase verb — GET, POST, PUT, DELETE.
string
The request path only — no scheme, host or query — with its leading slash, and byte-identical to the path you actually send, including casing. Sign the decoded form: if your URL contains percent-encoded segments, /x/a%20b is signed as /x/a b.
string
Empty string when there is no query. Otherwise: split into key/value pairs; sort by key in byte order, ties broken by value; re-encode keys and values with RFC 3986 percent-encoding (uppercase hex, space as %20, never +); join as key=value with &.Byte order puts uppercase before lowercase — Z sorts before a. A locale-aware or case-insensitive sort will produce a different string and fail verification.
string
Byte-identical to the Timestamp header. Seconds, not milliseconds.
string
Byte-identical to the Nonce header. Must parse as a UUID; use lowercase 8-4-4-4-12.
string
Your service account’s API secret. It is part of the signed content — it is never sent as a header.
string
Lowercase hex SHA-256 of the raw request body bytes. An empty body hashes to e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
Sign the UTF-8 bytes of that string with RSASSA-PKCS1-v1_5 over SHA-256, then base64-encode the result.
Sign the path you send. The gateway rewrites paths internally before forwarding, and it carries the path you signed across that rewrite for you. So you always sign the public /v1/... path — never an internal one.

A worked example

What a signed request looks like on the wire

Troubleshooting

Every rejection carries a code in its problem+json body. Branch on that, never on detail.
One or more of the four headers is missing. Partial header sets are treated the same as no credentials at all.
You sent an Authorization: Bearer … header. The public API does not accept dashboard session tokens, and sending one alongside correct signature headers is rejected just the same — the token is never parsed, so whether it is valid makes no difference. Remove the header and sign the request.
All four headers were present, but the signature did not verify. In order of likelihood: the body you hashed is not byte-identical to the body you sent (re-serialising JSON between hashing and sending is the usual cause); the query string was not canonicalised; you signed a different path than you sent; or your clock is more than 60 seconds out.
Nonces are single-use, so a replayed one fails exactly like a bad signature. Generate a fresh UUID for every attempt, including retries.
The signature verified — this is never a signing problem. Your service account lacks the permission the operation requires; grant it in the Developer Hub and retry. See Service accounts.
The signature and the permissions are both fine. The operation belongs to a feature that is not provisioned for your account, so no permission grant will open it — ask your StableMint contact to enable it.

Next

Service accounts

Create credentials and grant permissions.

Quickstart

Make your first call.