Resources
Security
How the partner API is secured and what each control enforces. Day-to-day key handling is in Authentication; a questionnaire map is at the end.
The credential
Section titled “The credential”The complete API key, sent as x-api-key, is the credential. We store only a
SHA-256 hash and the public lookup prefix, and never persist or log the key,
so it is shown once. SHA-256 needs no password KDF because the secret is 256
random bits.
A key is bound to one organization. avvio_test_ and avvio_live_ keys
resolve to separate organizations, and the environment comes from the key’s
prefix, never a database column, so a stale row cannot promote a test key to
live money.
Limiting what a leaked key can do
Section titled “Limiting what a leaked key can do”Three settings are fixed at issue, enforced server-side on every request, and carried unchanged through rotation. No endpoint edits them, so issue a second key rather than widening the first.
| Control | Also called | What it enforces |
|---|---|---|
Scopes: read / write, plus the consents crypto_payouts and refunds |
Scoped tokens, least privilege | A read key is refused any method but GET, and refused full account details even on a GET. A consent is off until granted; refunds can be granted only by an owner or admin |
| Allowed source addresses | IP allowlisting, IP pinning | A request from any other address is refused with KEY_IP_NOT_ALLOWED |
| Expiry | Credential lifetime | 365 days by default, 730 at most |
There are no per-resource scopes, because a permission matrix is easy to get
subtly wrong and “reads only” can be verified from the HTTP method. The two
consents come only with write: crypto_payouts spends from your own wallet,
and refunds returns a buyer’s money. For a finer boundary, use a separate
organization. Source addresses must be exact; issuance rejects a CIDR block.
A key authenticates as a machine member of your organization behind a default-deny route allowlist, which only the payout surface has joined. Team management, KYB documents, provider terms, wallet operations and key management are unreachable, so a stolen key cannot mint more. The API reference is the complete list of what a key can touch.
Rotation and revocation
Section titled “Rotation and revocation”Rotation (POST /organizations/{orgId}/api-keys/{apiKeyId}/rotate, from a
signed-in dashboard session only; an API key cannot call it) mints a successor and gives the predecessor a
deadline, 24 hours by default; both work during the overlap. The successor
keeps the scopes and addresses, so rotation cannot turn a read-only key into a
spending key, and gets a full lifetime, so rotation never shrinks a key into
its own overlap window.
Rotation refuses an expired or revoked key, which it would otherwise revive.
A second rotation, such as a retry after a timeout, returns
KEY_ALREADY_ROTATED with the existing successor’s id.
Issuing, rotating and revoking happen in the dashboard, so rotation cannot be
automated end to end. Revocation is immediate. Each key’s event trail
(issued, rotated, revoked) names the actor and any successor; expiry
records no event. If a key may be exposed, revoke it, issue a successor, and
tell [email protected].
Payout controls
Section titled “Payout controls”A key’s machine member holds the operator role, which may propose money
movement but cannot sign a treasury transfer; owner and admin members sign
on their own passkeys.
Above an amount threshold, your organization can require M-of-N approval on
API payouts. POST /payouts and batch confirm then answer 202 with an
approval, a batch holds at awaiting_confirmation whatever autoCommit said,
and nothing is priced or sent until owner or admin members reach quorum in
the dashboard. Votes need a human session and the initiator cannot approve
their own request, so a leaked write key can only propose
(Approvals).
Every payout path is gated server-side: POST /payouts, batch confirm,
quote-and-accept, and payout links at mint and submit. With a threshold or cap
on, quote-and-accept answers 403 USE_POST_PAYOUTS, so POST /payouts is the
one door for API calls. Payout links answer USE_POST_PAYOUTS only when the
amount would need approval, and enforce caps at submit
(422 PAYOUT_LIMIT_EXCEEDED).
| Control | On | What it enforces |
|---|---|---|
Route allowlist, write scope, operator role |
Always | Required by the payout routes |
Idempotency-Key |
Always | Replays a retry; a 15-minute detector catches the same body under a new key (Idempotency) |
| Source addresses, expiry | Always | Per key |
| Approvals | With a threshold | M-of-N human approval |
| Velocity caps | When we set them, at your request | Per payout, per day and per end user per day, in USD, on USD-sourced payouts only today. 422 PAYOUT_; nothing is sent |
| Screening | Always | Destination-country list; sanctions and name screening delegated. 422 PAYOUT_REFUSED, final |
| Purpose of payment | Always | purposeOfPayment required for INR, GHS, CNY and BRL, validated against the corridor’s catalog, never defaulted |
Audit trail
Section titled “Audit trail”GET /payments/organizations/{orgId}/audit-events records every audited
mutation, succeeded or refused: payouts created or canceled; batches
submitted, confirmed or canceled; approvals decided; recipients created,
changed or deleted; keys issued, rotated or revoked; webhook endpoints created,
deleted, paused, re-signed or replayed. Each row carries the key prefix or
dashboard user, the source IP, the outcome, the error type on a refusal, and
the requestId that matches x-request-id and error bodies. The API never
returns a key’s internal id, and a row holds no request body or bank field.
Results are newest first, cursor on id, filterable by action,
resourceId, apiKey (prefix), actorUserId, createdAfter and
createdBefore. A read-only key can read it, so your SIEM needs no credential
that moves money. A row is written after the request settles, outside the
money transaction; the payout and its event prove the movement.
Data protection
Section titled “Data protection”The API is served over HTTPS. Personal data is encrypted at rest field by field with AES-256-GCM, under a data key wrapped by AWS KMS, and bound by additional authenticated data to its row, so a copied ciphertext does not decrypt against another record.
| Encrypted | Holds |
|---|---|
| Recipient bank details | The full destination account: CLABE, IBAN, account and routing numbers |
| Bank details on invoices and checkout links | Account number, routing number, nostro account |
| KYB documents, reviews and research | Fields read from uploaded documents (government id number, date of birth, full name, tax id, address), review findings with their evidence, and public-register research on officers and beneficial owners |
| KYB associated persons | Tax id, government id number, date of birth, email, phone, address |
| KYC profiles | Tax id, phone, address |
| Custom bank accounts | Account number, ABA routing number, nostro account |
Records a payment network sends back, and contact fields on your organization
and its members, are stored as received; ask about any field. Listings return
last4; full details are a separate call a read-only key is refused. The API
key is never returned after creation. A webhook signing secret is returned
once, when the endpoint is created and when its secret is rotated, and never
again.
| Data | Kept | Then |
|---|---|---|
Batch line instruction (items and CSV export) |
90 days after the batch ends | null; status, errors and payoutId stay |
Payout link sender (endUser.name, endUser.email) |
Until the receipt email is sent, or the link expires unused | Removed from the link; the address the receipt went to is kept as the delivery record |
| Webhook delivery log | 30 days after delivery or exhaustion; pending retries kept | Deleted |
Idempotency-Key records |
7 days; unresolved ones until support resolves them | Deleted |
| Recipients | Until you delete them | Soft-deleted, unpayable (Recipients) |
| A recipient’s single payment method | Until you delete it | Deleted (the row is erased) |
| Payouts and their events | Retained | Never deleted through the API |
| Audit events | At least five years (BSA record-keeping) | Nothing deletes them today |
Webhook authenticity
Section titled “Webhook authenticity”Deliveries are signed with Standard Webhooks (HMAC-SHA256); Webhooks covers verification. An endpoint URL must be HTTPS without credentials. Its address is resolved at registration and pinned for the connection, so a hostname cannot pass the check with one address and connect to another. Deliveries time out after 10 seconds.
Dashboard access
Section titled “Dashboard access”Keys are managed in the dashboard, so it is part of your attack surface.
Members sign in with a passkey (WebAuthn) bound to the origin, with no password
to phish; signup verifies the email with a one-time code.
Roles (owner, admin, operator, viewer) are enforced server-side.
The dashboard signs money-moving requests in the browser with the session’s own key, over the method, path, body, idempotency key, timestamp and nonce. The server requires a fresh timestamp and a single-use nonce, held by a uniqueness constraint. The partner API never asks for a signature.
Rate limits apply per credential and per source address; the numbers are in Environments.
What we do not do
Section titled “What we do not do”- No OAuth 2.0 or OpenID Connect: there is no end-user consent step to solve.
- No request signing or mTLS on the partner API. Anyone holding the key can use it from any allowed address, and we cannot detect a request altered by a proxy you control.
- No per-resource scopes, no editing a key in place, and no API-driven issuance or rotation.
- No published egress addresses; verify our deliveries by signature.
- No SSO, SAML or SCIM, so no directory integration or automated deprovisioning.
- No streaming audit export; poll the audit trail.
- No name screening of our own. It is delegated in writing to the payment network; ask for the delegation letter during your review.
We use a bearer key because every HTTP client supports it. If your risk assessment needs one of the above, raise it before you integrate. Keeping the key from leaking is on your side: Authentication has the rules.
Reporting a vulnerability
Section titled “Reporting a vulnerability”Email [email protected] and do not disclose until a fix has shipped. We acknowledge within 2 business days, assess severity within 5, and credit you when the fix ships unless you ask us not to. Anything that requires a compromised API key is out of scope.
If you are filling in a security questionnaire
Section titled “If you are filling in a security questionnaire”| The questionnaire asks about | Answer |
|---|---|
| OAuth 2.0 / OIDC, SSO / SAML / SCIM | Not offered |
| Scoped tokens, IP allowlisting | Scopes and allowed addresses |
| API key rotation | Rotation with overlap |
| Credential storage at rest | SHA-256 hash |
| Encryption at rest and in transit | Field-level AES-256-GCM, HTTPS |
| Webhook authenticity | HMAC-SHA256 |
| Our egress addresses | Not published |
| MFA, role-based access | Passkeys and four roles |
| Audit logging | Audit API, kept at least five years |
| Rate limiting | Environments |
| Vulnerability disclosure | [email protected] |
Was this page helpful?