The Spritz API is primarily a backend-to-backend integration. In most cases your
servers talk directly to Spritz, acting on behalf of your users,
and you authenticate those requests by signing them with HMAC.
There are also times when you want a user’s own device (their browser or mobile app)
to call Spritz directly. For those cases you mint a short-lived token on your backend
and hand it to the device, which then uses it to authenticate its own requests.
Acting on behalf of your users
Almost everything in the Spritz API happens on behalf of one of your users. When you
create an off-ramp, connect a bank account, or fund a card, you’re doing it for a
specific user, and your credentials tell Spritz which user that is.
The exception is the integrator endpoints. Those are about you, not your users.
They cover things like your integrator profile, your API keys, and your webhook
configuration. A simple way to hold the distinction: user endpoints act on your
users’ resources, and integrator endpoints manage your own integrator account.
Because integrator endpoints aren’t tied to a user, you don’t send a user API key
with them. You sign them with your integrator credentials only (the three signing
headers below, without the Authorization header).
Backend to backend with HMAC
This is the primary way to use the Spritz API. Your backend signs each request with
HMAC-SHA256, so Spritz can confirm it really came from you and that nothing was
changed in transit.
Signing produces three headers. Alongside them you send Authorization to identify
the user you’re acting for (this one isn’t part of the signature).
How the signature is built
You sign a canonical string that pins together the timestamp, method, path, and a hash
of the body:
timestamp is the same value you send in X-Timestamp
METHOD is the HTTP method in uppercase (GET, POST, and so on)
path is the request path including its query string, with query params sorted by
key and URL-encoded (for example /v1/off-ramps?limit=10&status=pending)
bodyHash is the SHA-256 hex digest of the raw request body, or an empty string when
there’s no body
Example
Here’s the full signing algorithm. It uses the Web Crypto API, so it runs as-is on
Node 18+, Bun, Deno, Cloudflare Workers, and browsers.
Then send the signed headers along with the Authorization header for the user:
Your timestamp has to be within 5 minutes of Spritz’s server time, otherwise the
request is rejected. This keeps a captured request from being replayed later.
From a user’s device with an integrator token
Sometimes you’d rather have the user’s device call Spritz directly instead of routing
everything through your backend. For that, your backend mints a short-lived integrator
token (it starts with spr_) and passes it to the device. The device then sends it in
the Authorization header:
Because the token is short-lived and minted per user, you can safely hand it to a
browser or mobile app without exposing your backend credentials.
When something’s wrong
A missing or invalid credential returns a 401. A valid credential that isn’t allowed
to touch a particular resource returns a 403. Both come back as
problem responses you can parse the same way as any other error.