Skip to content
VertexBridgeDocs
On this page

Authentication

Every request carries an API key in the X-API-Key header. There are no sessions, no OAuth and no request signing.

Put the whole key — everything, including the part after the dot — in X-API-Key. The examples on these pages read the base URL from VERTEXBRIDGE_API_BASE: set it to the base URL for your key's environment (see Live and test).

GET /v1/partner/me
curl $VERTEXBRIDGE_API_BASE/v1/partner/me \
  -H "X-API-Key: $VERTEXBRIDGE_API_KEY"
Not the Authorization header.

A key sent as Authorization: Bearer … is not read at all, and the request fails with 401 unauthenticated and the message api key required. Keys have a header of their own so that they can never be confused with any other kind of credential.

Anatomy of a key

A key has two parts, joined by a dot:

Example — not a real key
vb_test_abc123.REPLACE-WITH-YOUR-OWN-KEY
  • The prefix, before the dot: vb_live_ or vb_test_ followed by six characters. It identifies the key and is safe to show, and it is how we refer to a key.
  • The secret, after the dot. We store only a one-way hash of it. It is shown to you once, when the key is issued, and nobody at VertexBridge can show it to you again — there is no reveal, and no support exception. A lost key is replaced, not recovered.

Keys are issued to your team during onboarding, each with the scopes you ask for. To rotate one, ask us for a new key, deploy it, then ask us to revoke the old one; both work until the old one is revoked, so rotation needs no coordinated cut-over.

Live and test

Key prefixSend it toMoves real money
vb_live_https://api.vertexbridge.io — not serving yetYes
vb_test_https://sandbox.vertexbridge.io — not serving yetNo

Every VertexBridge API server serves exactly one environment, and accepts only that environment's keys. A vb_test_ key presented where live keys are served, or a vb_live_ key where test keys are, is refused with 401 unauthenticated and the message invalid api key — so a test key can never move real money.

That is deliberately the same answer an unknown key gets. If a test server told you a live key was real but in the wrong place, anyone holding a stolen key could test it there for free. So when a key you know is valid is refused, check first that it is the right key for the server you are calling. Keep the two keys in separate configuration, so that a deploy can never pair a live key with test code or the other way round.

Before activation

A new account starts as pending and is activated once we have completed our checks on your business. While it is pending, its vb_test_ keys work normally, so you can build and test your integration straight away. Its vb_live_ keys are refused with 403 permission_denied until the account is active. /v1/partner/me tells you which state you are in.

Before your first live key

A live key is issued only once you have signed our API agreement. Test keys need no agreement, so you can build and test your integration before you sign. The order is: test keys, then the agreement, then live keys.

If we publish a new version of the agreement, you sign it again before any further live key is issued. Live keys you already hold keep working in the meantime.

Signing is done by a person, in the dashboard. Your own systems can check where you stand with GET /v1/partner/agreement — before a deploy that expects to issue or rotate a live key, for example. It needs only the read scope.

GET /v1/partner/agreement — 200 OK
{
  "signed": true,
  "current_version": "2026-11-v3",
  "signed_at": "2026-11-06T09:14:02Z"
}
  • signed is about the version currently in force, not whether you have ever signed. When we publish an amendment it goes back to false until you sign again — and live keys you already hold keep working throughout.
  • current_version names that version; signed_at is when you signed it, and is null while you have not.
  • The agreement's text is not in the response. Read it in the dashboard.

Scopes

A key carries only the scopes it was issued with, so a key that reads your balance cannot move money just because both belong to you. An endpoint that needs a scope your key lacks answers 403 permission_denied.

ScopeAllows
readReading your account, balance and webhook deliveries.
purchaseBills and VTU purchases.
payoutTransfers out to a bank account.
cryptoBuying and selling crypto.
identityIdentity verification.
accountsCreating virtual accounts for your customers.

Each scope covers one kind of action, and they are deliberately narrow. accounts is separate from purchase, for example: the part of your system that creates a customer's virtual account and the part that buys airtime are usually different services, and a leaked key for one should not be able to do the other. Ask for the scopes each service needs, and give each service its own key.

These are enforced, not advisory. A key without payout cannot send a payout, or even look up who owns an account, whatever else it carries — it gets 403 permission_denied. The endpoints that exist today, and the scope each one requires:

EndpointScope required
GET /v1/partner/meAny valid key
GET /v1/partner/balanceread
GET /v1/partner/catalogue/{product}read
GET /v1/partner/catalogue/{product}/{biller_code}read
GET /v1/partner/agreementread
POST /v1/partner/purchases/airtimepurchase
POST /v1/partner/purchases/datapurchase
POST /v1/partner/purchases/electricitypurchase
POST /v1/partner/purchases/cablepurchase
POST /v1/partner/purchases/exam_pinpurchase
GET /v1/partner/purchases/{reference}purchase
GET /v1/partner/deliveriesread
GET /v1/partner/transactionsread
POST /v1/partner/deliveries/{delivery_id}/replayread
GET /v1/partner/webhook-signingread
POST /v1/partner/payouts/enquirypayout
POST /v1/partner/payoutspayout
GET /v1/partner/payouts/{reference}payout
GET /v1/partner/payouts/bankspayout

Check a key

GET /v1/partner/me moves no money and needs no scope, so it is the call to make first — after a deploy, after a rotation, or when something fails and you want to rule the key out:

200 OK
{
  "partner": "your-platform",
  "name": "Your Platform Ltd",
  "status": "active",
  "allowed_products": [],
  "scopes": ["read"],
  "rate_limit_per_minute": 60
}
  • allowed_products lists the products enabled on your account. A product call fails with 403 until it is listed here.
  • scopes are this key's scopes — not your account's. Two of your keys can answer differently.
  • rate_limit_per_minute is your account's ceiling. See rate limits.

When authentication fails

StatusCodeMessageMeans
401unauthenticatedapi key requiredNo X-API-Key header reached us.
401unauthenticatedinvalid api keyThe key is unknown, revoked, expired or malformed — or it is a valid key for the other environment.
403permission_deniedthis key does not carry the read scopeThe key is valid; this endpoint needs a scope it lacks.
403permission_deniedpartner account is suspended; contact your account managerThe key is valid but your account is suspended or closed. The word after is is the account's state: suspended or closed. Every key is refused, live and test.
403permission_deniedpartner account is pending activation; test keys only until it is activeA vb_live_ key on an account that is not yet activated. Its test keys work — see before activation.
401 Unauthorized
{
  "code": "unauthenticated",
  "message": "invalid api key",
  "correlation_id": "7ebfa718430843ae94bd61626d2a07be",
  "details": {},
  "retry_after_seconds": null
}

invalid api key is deliberately the same message for every way a key can be wrong. Telling an unknown key from a revoked one would let anyone holding a guessed key learn which of our partners exist. Check, in order: the header name, that the whole key was sent including the part after the dot, and that it is the key for this host.

Rate limits

Each account has a requests-per-minute ceiling, shared by all of its keys and returned by /v1/partner/me. Over it, a request is answered 429 rate_limited with a Retry-After header and the same number in retry_after_seconds. Wait that long, then retry. The limit is set per account — tell us if yours is too low for your traffic.

Keeping keys safe

  • Call the API from your servers. A key in a browser, a mobile app or a public repository is a key someone else has.
  • Load keys from a secret store or environment variable, never from source code.
  • Never log the whole key. Log the prefix — it identifies the key, and it is all we will ever ask you for.
  • If a key may have leaked, tell us and have it revoked. Revocation takes effect on the next request.

Next: Errors — the one error shape, and every code you can receive.