Quick Start
Get an API key and make your first authenticated request.
This guide takes you from nothing to a working request against the gateway.
Get an API key
Keys are minted by an admin through the key‑management API. Ask your Bitcall admin (or, if you are one, see Key Management) for a key scoped to what you need.
You will receive two values:
| Value | Example | Notes |
|---|---|---|
| Key id | ak_test_1a2b… | Sent on every request as x-key-id. |
| Secret | 9f83… (128 hex chars) | Sent on every request as x-api-secret. Shown once — store it securely. |
The secret is displayed only at creation (and rotation). It is never retrievable again. If you lose it, rotate the key.
Know the base URL
Send every request to the Bitcall gateway:
https://api.khalil.chatup.chAll product endpoints live under /v1/*. The examples below use this URL; if you
are running a gateway locally, substitute its address.
Make your first request
Send both headers. Nothing else is required — a read‑only catalog endpoint is the cheapest way to confirm the key works:
curl 'https://api.khalil.chatup.ch/v1/otp/countries' \
--header 'x-key-id: ak_test_1a2b…' \
--header 'x-api-secret: 9f83…'A valid key returns 200 and the list of countries. An invalid or expired one returns 401 with error.code set to INVALID_API_KEY — the gateway never says which part was wrong.
In code it is an ordinary request — no wrapper, no crypto:
const BASE_URL = 'https://api.khalil.chatup.ch';
const res = await fetch(`${BASE_URL}/v1/otp/countries`, {
headers: {
'x-key-id': process.env.KEY_ID,
'x-api-secret': process.env.SECRET,
},
});
console.log(res.status, await res.json());KEY_ID=ak_test_... SECRET=... node first-request.mjsCall a product
The same two headers work on any endpoint your key is scoped for. A POST adds only a body and its content type:
curl -X POST 'https://api.khalil.chatup.ch/v1/otp/request' \
--header 'x-key-id: ak_test_1a2b…' \
--header 'x-api-secret: 9f83…' \
--header 'Content-Type: application/json' \
--data '{"service_id":"SP-RU-telegram"}'If the key is not permitted to make this call, the gateway returns 401 with error.code set to INVALID_API_KEY — the same answer as a wrong secret, so check both.
Optional: a small helper
Since the credentials are the same on every call, most integrations wrap them once. The product guides use an api() helper of this shape:
const BASE_URL = process.env.BITCALL_BASE_URL ?? 'https://api.khalil.chatup.ch';
export async function api(method, path, { query, body } = {}) {
const url = new URL(BASE_URL + path);
for (const [k, v] of Object.entries(query ?? {})) {
if (v !== undefined) url.searchParams.append(k, v);
}
const res = await fetch(url, {
method,
headers: {
'x-key-id': process.env.KEY_ID,
'x-api-secret': process.env.SECRET,
...(body ? { 'Content-Type': 'application/json' } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) throw new Error(`${res.status} ${(await res.json()).error}`);
return res.json();
}