Bitcall logoBitcall API
The Gateway

Get & Manage Keys

Create a key from your panel, capture the secret, and rotate or revoke it later.

You issue your own keys from your panel — there is nothing to request and nobody to wait for. This page walks you from a panel account to a working key, then through managing it afterwards.

For what a key is — key id versus secret, environments, what a key carries — see API Keys & Secrets. This page is the how-to.

Before you start

You need an account on your panel, and you need to be logged in.

Self-service key management is enabled per account, so it is not switched on everywhere. If you cannot find the section described in the next step — not in the avatar menu, not on your profile — then it is not enabled for your account. Contact your provider and ask for API key access; it is not something you can turn on yourself.

Ask them at the same time which environment you should be issuing keys for, and what calling it costs. Your panel issues the credential; only your provider can tell you which environment that credential reaches and how usage on it is billed. Get that in writing before your first paid call — see Environments and billing below.

Open the API keys section

Keys live in your profile, under API Keys. There are two ways in:

  • From the avatar menu. Click your avatar in the top-right corner, then API Keys.
  • From your profile. Open your profile and select the API Keys tab.

Both land on the same page — /<language>/profile/api-keys, for example /en/profile/api-keys.

If neither the menu item nor the tab is there, key self-service is not enabled for your account. That is the symptom to report to your provider.

Create the key

Click Create key in the top-right of your panel — or Create your first key if you have none yet. The Create API key dialog opens with two fields.

Scopes

Scopes decide what the key is allowed to do. At least one is required, and the dialog starts with otp:read already selected. Options are grouped by product — OTP, eSIM, HLR / Lookup — and you can select as many as you need.

ScopeGrants
otp:readEvery read on the OTP client surface — catalog, history, order status.
otp:writeEvery OTP mutation — renting a number, cancelling, resending, reactivating.
esim:readEvery read on the eSIM client surface — plans, orders, profile status.
esim:writeEvery eSIM mutation — buying a plan, and resending an order confirmation.
hlr:readEvery read on the HLR client surface — pricing, job status, results.
hlr:writeEvery HLR mutation — submitting a lookup, cancelling one, and building an export.

The model is uniform across products: <service>:read covers the reads, <service>:write covers the writes. Grant both for full access to a product, or one alone for a read-only or write-only key. Granting nothing for a product means the key cannot touch it at all.

These six are the only scopes a key of yours can hold. Higher-privilege scopes exist for operators, but a client key can never carry one — the gateway drops them when it computes the key's effective scopes, even if one were somehow granted.

Allowed IPs / CIDRs

Optional, and comma-separated. Leave it empty and the key works from any address; fill it in and requests from anywhere else are rejected.

Set it on anything production. The secret travels on every request, so it reaches every proxy log and trace along the way — pinning the key to your servers' addresses means a secret read out of one of those logs cannot be used from an attacker's own machine.

The allowlist is matched against the address the gateway actually sees. Behind a proxy or NAT, list the egress addresses your traffic leaves from, not your internal ones.

An allowlist narrows where a leaked secret works — it does not make one safe. Anything able to send from a listed address can still use the key: another process on those hosts, a workload sharing your NAT or egress gateway, or an attacker who has reached one of them. Treat it as blast-radius reduction, not containment — rotate on any suspected leak regardless of whether an allowlist was set.

What you do not choose

Three properties are set server-side and are not in the dialog: the key's role, the account it is bound to, and its rate limit. A key you create can only ever act as your own account — it cannot be pointed at another one.

Click Create key to issue it.

Copy the secret — you get one chance

The Key created — copy the secret now dialog shows the two values, each with a copy button:

FieldFormat
Key IDak_live_… or ak_test_… — the prefix, then 64 hex characters.
Secret (shown once)128 hex characters.

Store the secret before you click Done. There is no reveal endpoint and no support request that can recover it — the gateway keeps only an encrypted copy it can verify against, never one it can show you. Closing this dialog discards the secret permanently, and the only way back is to rotate the key for a fresh one.

Put it straight into a secret manager or your deployment's environment configuration. Do not paste it into a ticket, a chat message, or a file you will commit.

Make a request

Send the key id and secret as two headers. A read-only catalog endpoint is the cheapest way to prove the key works:

curl 'https://api.bitcall.io/v1/otp/countries' \
  --header 'x-key-id: ak_test_1a2b…' \
  --header 'x-api-secret: 9f83…'

This endpoint needs otp:read, which is the scope the create dialog preselects — so a key made with the defaults runs it unchanged. If you deselected otp:read, call something your key's scopes actually cover instead.

A working key returns 200 and the country list. A wrong, revoked or unauthorized one returns 401 with error.code set to INVALID_API_KEY.

See Authentication for the full contract and the order the gateway checks things in.

Your key list

Once you have keys, the page lists them:

ColumnWhat it shows
Key IDShortened for readability, with a button that copies the full value.
ScopesThe scopes granted at creation.
StatusActive or Revoked.
CreatedWhen the key was issued.
Last usedRoughly when it last authenticated a request, or Never. Stamped at most once a minute per key, on a best-effort basis — see the note below.

You can narrow the list by Status and by scope, and the table pages once you have more than ten keys. Last used is a coarse signal, not an audit trail: the gateway stamps it at most once a minute per key, and the write is best-effort — it is skipped rather than retried if it fails, so a key that served traffic can still show an older time. It is good for spotting a key nothing calls any more; it is not a way to confirm a deployment has picked up a new credential. Use your own application logs for that.

Rotating a key

Rotation replaces a key with a fresh one. It has no overlap window, so which of the two procedures below you want depends on whether you are reacting to a compromise or doing planned maintenance.

Click the rotate icon on the key's row and confirm in the Rotate this key? dialog. What happens:

  1. A new key id and a new secret are issued, carrying over the key's scopes, IP allowlist, rate limit, label and — if it had one — its original expiry date. Rotation is not a way to extend a key's lifetime.
  2. The old key is revoked immediately — it stops authenticating the moment you confirm.
  3. The new secret is shown once, in the same one-time dialog as at creation.

Rotation issues a new key id, not just a new secret. Updating only x-api-secret in your app will not work — you must update both headers.

Suspected compromise — rotate now, accept the gap

If you think a secret has leaked, rotate immediately. The new pair does not exist until you confirm and the old key dies at that same moment, so there is no instant when both work: every call still carrying the old credential fails from then until your deployment ships the new one. That gap is the price of cutting off whoever else holds the secret, and it is the right trade — take the failing requests.

Keep it short: capture the new pair from the dialog, deploy both values together, and confirm the dialog only when someone is ready to deploy — not hours ahead.

Planned change — deploy a second key first, then revoke

For scheduled maintenance there is no reason to accept a gap. Do not use rotate.

  1. Create a second key with the same scopes and IP allowlist.
  2. Deploy it, so your application is calling with the new credential.
  3. Confirm the old key has gone quiet — from your own application logs, not from the Last used column, which is too coarse for this (see Your key list).
  4. Revoke the old key.

Two keys can be active at once, which is what makes this seamless where your panel offers key creation. If self-service creation is not enabled for your account, this procedure is not available to you and rotation with its gap is the only route — ask your provider whether they can issue the second key for you.

Rotation is still the right tool for a compromise, because a second-key rollout leaves the leaked credential working while you deploy. Speed matters more than uptime there.

Revoking a key

Click the revoke icon and confirm in the Revoke this key? dialog. The key stops authenticating at once, permanently — there is no undo, and anything still using it starts failing immediately.

Rotate and revoke are both disabled on a key that is already revoked.

Environments and billing

The key id starts with ak_live_ or ak_test_. That prefix records which environment the key was issued for, and it is useful for telling two credentials apart in your configuration.

A prefix is not a promise that calls are free or simulated. ak_test_ does not by itself mean a sandbox, a mock provider, or an unbilled request. Whether a given environment reaches real providers, sends real messages, provisions real eSIMs, or draws down real balance is a property of that environment, not of the string in front of your key id — and only your provider can tell you which is which for your account.

So before the first call that could cost you something, get these from your provider and keep them with your credentials:

AskWhy it matters
Which environment this key reachesDecides whether a purchase is real.
Whether calls on it are billed, and at what rateA "test" key on a live environment spends real balance.
Whether providers are real or simulatedDecides whether an OTP number really rents and an eSIM really provisions.
Which base URL goes with itA key issued for one environment does not authenticate against another.

Treat anything you have not confirmed as live and billable. The paid endpoints publish no idempotency key, so a call you did not mean to make cannot be undone by repeating it — see Conventions.

Rate limits

Above the list, your panel shows your account rate limit — for example 1000 requests / 60s.

That quota is your account's, and every key you hold spends from the same allowance. Two keys do not get you twice the requests — they get you two credentials sharing one allowance. Issuing more keys is a way to separate your integrations, not a way to buy throughput. If you need a higher limit, ask your provider; more credentials will not give you one.

See Rate Limits for what happens when you exceed it.

When a call is rejected

A 401 with error.code set to INVALID_API_KEY covers several situations deliberately, so work through them in order:

CheckWhat to look for
Is the key still active?Find it in the list — a Revoked key, or one you rotated away from, fails every request.
Are both headers right?x-key-id and x-api-secret, each single-valued. A duplicate header is ignored, which reads as absent.
Is the secret the current one?After a rotation the old secret is dead. Confirm your app has the pair from the most recent dialog.
Are you calling from an allowed address?If the key has an IP allowlist, a request from anywhere else is refused. Check the egress address your traffic actually leaves from.
Does the key hold the scope?An operation outside the key's scopes is refused the same way. Compare the endpoint against the Scopes column.

These all answer identically on purpose — distinguishing them would let someone probe which key ids are real without holding a secret. Every response carries a requestId; quote it to your provider's support and the exact failing check can be confirmed from the server side. See Errors.

Sending no credentials at all is the one case that differs: that returns 401 AUTHENTICATION_REQUIRED.

If you get 400 VALIDATION_ERROR instead, your key was accepted: credentials are checked before the request itself. Fix the fields named in error.fields.

Best practices

  • One key per integration. Separate keys make revoking or rotating one system surgical instead of an outage for everything that shared a credential.
  • Grant the narrowest scopes that work. A key that only reads cannot be used to spend money. Add :write only where the integration genuinely writes.
  • Set an IP allowlist on production keys. It is checked before the secret is verified, so a leaked key is unusable from any address you have not listed. It cannot help when the leak comes from inside that range — revoking the key is what ends that — so treat it as narrowing the blast radius, not closing it.
  • Test keys in development and CI, live keys only in production. The ak_test_ / ak_live_ prefix tells you which credential you are holding — not whether calling it is free. Confirm that separately with your provider; see Environments and billing.
  • Rotate on any suspicion, and on a schedule. Rotation is cheap; an unnoticed leak is not.
  • Never ship a secret in client-side code. Anything sent to a browser or mobile app is readable by whoever holds the device.

On this page