- Getting a token — proving who you are with your partner email and password and receiving a JWT.
- Authorization — what that token lets you do, and which rules each endpoint is gated by.
Before you start
You need a partner account — an email + password issued by the Blackswan integration team. If you don’t have one yet, contact hello@bsa.ai. Accounts come with the admin role attached, which is what every partner-facing endpoint requires. Token issuance lives on a separate auth host from the main API:
Your email and password are used in exactly one place: to mint a token
on the auth host. The resulting JWT is what you present, as a Bearer
token, on the main API. The main API never accepts the email and
password themselves — a request to
https://api-staging.bsa.ai that
carries them instead of a token is rejected with 401 unauthenticated.
1. Obtain a token
CallGET /v1/auth/token on the auth host with your partner email and
password. The endpoint returns a signed JWT. This is the only way to
obtain a token; there is no other issuance endpoint and no API key.
Credentials
Pass the email and password as the request’s user and password — every HTTP client has a built-in way to do this:curl -u email:password,
requests.get(..., auth=(email, password)), and so on. You do not
build any header yourself.
Example
Response
Token lifetime
Tokens are valid for 365 days from issuance (exp - iat = 31_536_000).
There’s no refresh endpoint — when expiry approaches, just call
GET /v1/auth/token again with your email and password.
Handling expiry
Pick whichever pattern fits your architecture; they’re not mutually exclusive.Proactive — decode the exp claim
The cleanest approach if you cache the token. JWTs are
header.payload.signature with each segment as base64url-encoded JSON.
Decode the payload, read exp (unix seconds), refresh when the
remaining lifetime drops below a threshold (e.g. 7 days).
Reactive — catch 401, refresh, retry once
Simplest pattern. No JWT parsing required, just one extra round-trip when expiry actually hits.- Don’t retry indefinitely — retry exactly once. If the second call also 401s, the credentials are wrong, not the token.
- A 401 can also mean the token was revoked or your account was disabled — same handler still works (you’ll get 401 again on retry, and surface it).
Scheduled rotation
For machine-to-machine integrations where you already have the email and password stored, the simplest possible setup is a cron that refreshes weekly or monthly. No expiry math, no retry logic — just a fresh token always ready./var/lib/bsa/token (or whatever store you use) on
every request. The cron ensures it’s never within 7 days of expiry.
Knowing when expiry is coming
If you operate the integration yourself rather than via cron, set a calendar reminder for ~11 months after each token issuance. Theiat claim in the JWT payload gives you the exact issuance timestamp
to anchor on.
Errors
2. How authorization works
Every partner-facing endpoint enforces:- Authentication — a valid, unexpired JWT in the
Authorizationheader. Failure:401 unauthenticated. - Authorization — the JWT must contain the
ADMINrole. Failure:403 permission_deniedwithsub_code: insufficient_role. A 403 is never fixed by refreshing the token — the account needs the role.
/v1/customers/..., /v1/loans/...
including the repayment routes under a loan, /v1/loan-products/...,
/v1/credit-scorecard/..., /v1/credit-boost) require the ADMIN
role. User management (/v1/users) is reserved for the SUPER_ADMIN
role and is not part of the partner surface.
3. Use the token
Set theAuthorization header on every request:

