1. Create a user
Create a user with their email address. Spritz returns a user API key (prefixedak_)
that you use to authenticate requests on that user’s behalf.
apiKey securely. You send it as the Authorization bearer for every request
you make on this user’s behalf (see Authentication). You can
also pass an optional timezone.
2. Verify identity
Before a user can move money, they complete identity verification (KYC). Start a verification session for the user:provider is either persona or plaid, and both sessionToken and
verificationUrl are nullable.
Use this dispatch contract:
Prefer the provider SDK when you support it and a token is present; otherwise use the
hosted URL. A non-null
verificationUrlExpiresAt is authoritative. If that time has
passed, create another session instead of opening the old URL. When it is null, still
request sessions just in time and never persist a URL as a permanent verification link.
Persona SDK references: Web embedded flow,
React Native and
native mobile. Plaid sessions
use the same Plaid Link client integration described by Plaid’s SDK documentation; the
token comes from this verification-session response, not from the bank-account
link-token endpoint.
Creating a session can also return 409. Branch on its stable code, never its
title or detail:
The user’s verification state is reported on their profile as
verification.status:
not_started, verified, failed, retry, or disabled. A retry status means
they can try again; create a new session to do so.
3. Read capabilities
GET /v1/users/me returns the user’s verification status and their capabilities.
Capabilities are the source of truth for what a user can do right now, and what’s
blocking anything they can’t.
fiat_to_crypto / ach_debit capability with no separate terms requirement. Other
products and regions can still return requirements_needed; always drive the UI from
the actual capability object instead of assuming every method has the same gates.
Select ACH debit by the exact pair, not by array position or display name:
method is optional in the generic capability schema because non-ramp products do not
have a transfer method. A missing method does not match ACH debit. If no capability
has the exact pair above, treat ACH debit as not_available: do not start bank linking,
and offer another supported deposit method.
Each capability is a product, and for ramps a method:
- Products:
fiat_to_crypto,crypto_to_fiat,bill_pay,crypto_card - Methods:
ach_credit,ach_debit,wire,sepa_credit_transfer,rtp,push_to_card,canadian_eft
status tells you where it stands:
For the ACH debit entry point, use this minimum copy and action contract:
“Contact support” means the integrator’s configured in-app support route. Its support
team escalates to Spritz; do not invent a direct Spritz operations URL.
When a capability is
requirements_needed, its requirements[] lists what to do.
type and status are always present. description, actionUrl, retryable, and the
capability’s nextRequirement are optional by contract.
Choose the requirement whose type equals nextRequirement when that field is present
and still incomplete. Otherwise choose the first requirement whose status is not
completed. If there is no such requirement, re-fetch the user once. If the capability
still says requirements_needed with no incomplete requirement, show “We couldn’t load
the next verification step.” with Contact support; do not guess a step.
Use a returned description when present. Otherwise use this fallback and action
matrix:
When
actionUrl is present, open it for every non-terminal requirement, then re-fetch
the user after the flow returns. A missing retryable is not permission to retry a
failed provider action automatically. Only retry when it is true or when the explicit
type/status handling above gives the user a retry action.
Drive your UI from capabilities
Render your onboarding and product UI off the capabilities list rather than tracking state yourself:- Show a capability as available when its
statusisactive. - When it’s
requirements_needed, select and render the requirement using the fallback rules above. Never assume its optional presentation or action fields are present. - After the user completes a requirement, re-fetch
GET /v1/users/me, or react to thecapabilities.updatedwebhook so you update without polling.
Connect an existing Spritz user
If the person already has a Spritz account, with you or with another integrator, use Integrator Connect to gain access with their consent instead of creating a new user. It’s an OAuth-style authorization flow.1
Create a connect session
From your backend, create a session with a pre-registered HTTPS redirect URI and an
optional
state value you can use to tie the result back to a request.2
Send the user to approve
Redirect the user to
authorizationUrl. They sign in to Spritz, review your
integration, and approve. The session is valid for about 10 minutes.3
Receive the authorization code
On approval, Spritz redirects the user back to your
redirectUri with a code and
the state you sent.4
Exchange the code for a user key
From your backend, exchange the code for that user’s API key. The code is
single-use and expires after a few minutes.
Next
Authentication
Use the user API key to sign requests.
What we support
Rails, tokens, and regions behind each capability.
Off-ramp
Put an active capability to work.
Webhooks
React to capabilities.updated and verification changes.