Skip to content

Buying data

POST /v1/purchases with your reference, the number and the bundle.

POST /v1/purchases HTTP/1.1
Host: api.poweredbyaura.com
Authorization: Bearer aura_live_…
Content-Type: application/json
{
"reference": "order-10293",
"phone_number": "+2348000000001",
"bundle_id": "bnd_ng_a_1gb_1d",
"metadata": { "customer_id": "cus_88412" }
}
Field Required Notes
reference Yes Your unique ID for this purchase: letters, numbers, -, _, . and :, up to 64 characters.
phone_number Yes E.164 format.
bundle_id Yes From List bundles.
network_id No Send it if you want Aura to refuse the purchase when the number is on a different network.
metadata No Up to 20 string values of your own, returned on the purchase and in webhooks. Do not put personal data here.

A new purchase returns 201 Created with the purchase in its current state. That is usually pending.

{
"id": "pur_01JA7ZK3Q9W2D8M5X4N6B1C0VE",
"object": "purchase",
"reference": "order-10293",
"status": "pending",
"phone_number": "+2348000000001",
"network_id": "net_ng_a",
"bundle": { "id": "bnd_ng_a_1gb_1d", "name": "1GB Daily", "data_mb": 1024, "validity_hours": 24 },
"price": { "amount": 35000, "currency": "NGN" },
"created_at": "2026-10-12T09:14:30Z",
"resolve_by": "2026-10-12T09:44:30Z",
"completed_at": null,
"delivery": null,
"failure": null,
"reversal": null,
"metadata": { "customer_id": "cus_88412" }
}

Store the id and resolve_by with your order. Do not tell your customer the data has arrived until the purchase is successful.

Status Code Meaning What to do
201 Accepted. Wait for the final result.
200 You already sent this reference with the same body. The original purchase is returned, with Aura-Idempotent-Replay: true. Treat it as the same purchase.
400 invalid_request, invalid_phone_number A field is missing or malformed. Fix the request.
401 invalid_api_key The key is missing, wrong or revoked. Check the key.
402 insufficient_balance Your available balance does not cover the price. Top up, then retry with the same reference.
403 ip_not_allowed The request came from outside your IP allowlist. Send from an allowed address.
409 reference_conflict The reference was used for a different purchase. Use a new reference.
422 bundle_unavailable, network_mismatch, unsupported_network The purchase cannot be made as asked. Refresh the catalogue or pick another bundle.
429 rate_limited Too many requests. Wait for Retry-After, then retry.
500 internal_error Something failed on Aura’s side. Retry with the same reference.
503 network_unavailable The network is not taking purchases. The error carries its live status. Retry later with the same reference, or offer another network.

Nothing is bought or held for any response other than 201. The full list is in Errors.

A timeout or a dropped connection leaves you not knowing whether Aura received the request. Retrying with the same reference is always safe: you get the original purchase back, never a second one.

const API = 'https://api.poweredbyaura.com/v1';
export async function buyData({ reference, phoneNumber, bundleId }) {
const body = JSON.stringify({ reference, phone_number: phoneNumber, bundle_id: bundleId });
for (let attempt = 1; attempt <= 5; attempt++) {
let res;
try {
res = await fetch(`${API}/purchases`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.AURA_API_KEY}`, 'Content-Type': 'application/json' },
body,
signal: AbortSignal.timeout(15_000),
});
} catch {
res = undefined; // Timed out or the connection dropped: safe to retry with the same reference.
}
if (res && res.status !== 429 && res.status < 500) {
return { httpStatus: res.status, body: await res.json() };
}
const waitSeconds = Number(res?.headers.get('retry-after')) || 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
}
throw new Error(`No answer for ${reference}. Look it up by reference before trying anything else.`);
}