Page cover
For the complete documentation index, see llms.txt. This page is also available as Markdown.

OAuth data sync

Overview

Use OAuth data sync to push bank data into a customer's stub business. The customer starts the connection in your portal, signs in to stub, selects a business, and grants consent.

stub never receives banking credentials or accesses banking screens.

Before you begin: You need a client ID, client secret, and allowlisted return addresses. Your integration contact issues these values. Use https://test.api.stub.africa for sandbox requests. Every endpoint on this page is relative to this URL. Sandbox and production credentials are not interchangeable.

How it works

You are a confidential OAuth client of stub's authorisation server. Your client secret is required whenever you issue, refresh, or revoke a token. Access tokens only authorise data sync requests.

Send the customer to stub's consent page. They sign in, select a business, and approve the connection. stub redirects them to a registered return address with a single-use authorisation code.

After approval, stub redirects the browser to your redirect_uri with code and state. If the customer declines, the redirect contains error=access_denied and state. It does not contain a code.

Protect authorisation codes with return addresses. stub matches the scheme and host exactly, and the path as a prefix. Ports must match too, except for http://localhost and http://127.0.0.1, where any port is accepted so a sandbox can be proved before real endpoints exist. Everything else must be HTTPS. Provide the exact URLs before go-live. Requests with unregistered URLs fail at /authorize.

Get tokens

Exchange the authorisation code from your server. Include the client secret in the request body or an HTTP Basic header. The secret is also required for token refreshes and revocations.

To refresh a token, send grant_type=refresh_token, refresh_token, client_id, and client_secret.

Store each new refresh token. Refresh tokens rotate. Using an older token revokes the token chain. The customer must then reconnect from your online banking portal.

The access token identifies one business. Each request applies to that business. Request payloads do not contain a business field.

Register accounts

Register the accounts the customer chose to share. Send a stable bank account ID in externalid. stub uses this value for matching and returns a bankaccountid for each account.

If a customer already added an account manually or through an aggregator, stub adopts it instead of creating a duplicate. When the externalid is one stub has not seen, it matches on the account number — but only against an account that is not already claimed by another externalid. An account already registered by another source is left alone. This request is idempotent. Send it again whenever the shared accounts change.

Push transactions

Send posted transactions only. stub validates each row on receipt and returns rejected rows in the response.

Treat any 2xx as accepted. This endpoint returns 202 when the accounting write is queued, and 200 when it completes while you wait. Which one you get depends on how the environment you are calling is configured, so do not branch on the exact code. A 202 response additionally means the batch is not yet written — use the receipt to find out what became of it.

Use GET /api/datasync/receipt/{receipt} to check a batch status. It returns accepted, ingested, declined, or failed. Use it after a timeout when the delivery result is uncertain.

Resolve rejected rows

Reason
What to fix

externalid is required

Include a stable transaction ID. stub uses it to identify resubmissions.

date must be yyyy-MM-dd

Posting date, no time component. Use datetime for the timestamp.

value (or debit/credit) is required

Send a signed value, or a debit/credit pair.

value may not be zero

A zero-amount row is not a movement of money.

description is required

Include the statement narrative shown to the customer.

A rejected row does not fail the batch. stub retains rejected rows for 30 days. An operator can return or replay a corrected row.

Handle retries and balances

Duplicate rows do not create duplicate transactions. Repeating a request with the same idempotency key returns the original response.

Repeat
How stub notices
What happens

The same row again

Its externalid already exists

stub records it as a duplicate. Nothing is written.

The same request again

Your Idempotency-Key matches one from the last 24 hours

stub returns the stored response. The handler does not run.

An Idempotency-Key can contain up to 128 characters. Longer keys return 400. stub does not truncate keys.

Include the running balance. It distinguishes valid, similar transactions from duplicates. Without it, a second matching transaction can be rejected as a duplicate.

The balance also checks transaction completeness. A break in the balance chain can indicate a missing transaction.

End a connection

Either side can end a connection. stub operators cannot end connections.

Revoke a connection

The endpoint returns 200 for unknown and already-revoked tokens. This prevents token discovery.

Handle customer revocations

Customers can disconnect your bank app in stub. stub then sends a signed webhook so you can stop pushing data.

Verify the signature with your client secret. No additional shared credential is required. Return 2xx to acknowledge the webhook. stub retries timeouts and 5xx responses three times with increasing delays. It does not retry 4xx responses.

Responses

Code
Means
What to do

200

Accounts were written, or a transaction batch was written while you waited

Store the bankaccountid values. For transactions, created and duplicates are in the body.

202

Transaction batch was accepted and is not yet written

Store the receipt. Review rejected rows in the body.

400

Request is malformed or Idempotency-Key is too long

Check the response body.

401

Token is missing, expired, or invalid

Refresh the token and retry once.

403

Customer revoked the connection

Stop pushing for that business. Do not retry.

413

Request contains more than 5,000 rows

Split the batch.

429

stub is saturated

Honour Retry-After and back off.

503

A stub operator paused data sync

Honour Retry-After. Tokens remain valid. Retry later.

Before go-live

  1. Provide the exact return addresses and unlink webhook URL. Both must use HTTPS.

  2. Store the client secret when it is issued. It is shown once. Two secrets can be active during rotation.

  3. Verify the webhook. stub sends a signed test request and records your response.

Last updated