AI Agent / Developer integration
Purchase redemption codes programmatically: after registering, generate an API key in one click in the Account (no review required; spending and top-ups accumulate a level score that auto-upgrades you across Lv1–Lv5 for level-based pricing). Balance orders are processed automatically; gift card orders may remain paid while their codes are being prepared.
Option 1: One-click MCP (recommended for AI Agents)
Paste the config below into any MCP-capable client (Claude, Cursor, etc.):
{
"mcpServers": {
"aibulk": {
"url": "https://aibulk.uk/api/mcp",
"headers": { "Authorization": "Bearer awk_your_key" }
}
}
}Claude Code users can run this directly:
claude mcp add --transport http aibulk https://aibulk.uk/api/mcp --header "Authorization: Bearer awk_your_key"Tools: list_products / check_availability / get_balance / create_topup / purchase_codes / get_order / list_orders / list_transactions. Orders and top-ups require reusable idempotency IDs. Server-side redemption is REST-only for now.
Option 2: REST API
Full API reference · llms.txt (integration guide for agents) · openapi.json
Balance-mode order example (returns the code list synchronously):
curl -X POST https://aibulk.uk/api/wholesale/v1/orders \
-H "Authorization: Bearer awk_your_key" \
-H "Content-Type: application/json" \
-d '{"externalOrderId":"your-unique-id","productId":"<product-id>","qty":1,"payment":"balance"}'Redeeming on your customer's behalf (server-side redemption)
If you collect the customer's details yourself and activate for them, do not use the redemption site's public endpoints (limited per source IP). Use these key-authenticated endpoints instead:
- GET /wholesale/v1/products/:id → redeemFields tells you what to collect before purchase (e.g. account_id = Claude Organization ID for prod_claude_pro); redeemInfo keys are redeemFields[].key.
- POST /wholesale/v1/redemptions {code, redeemInfo} → {redemptionId, status: pending}. Only codes from your own orders are accepted (otherwise 404 invalid_code). Limited to 60 submits/min per key.
- GET /wholesale/v1/redemptions/:redemptionId (or ?code=) → status not_redeemed | pending | processing | fulfilled | failed, plus retryable / failureCode. When failed, POST the same code again with corrected redeemInfo. Poll every 30–60s.
- Subscribe to redemption.fulfilled / redemption.failed webhooks for push; a resubmitted code emits a new event with a new eventId. Test end to end with prod_api_order_test: its codes are redeemed instantly through this API and emit redemption.fulfilled.
curl -X POST https://aibulk.uk/api/wholesale/v1/redemptions \
-H "Authorization: Bearer awk_your_key" \
-H "Content-Type: application/json" \
-d '{"code":"XXXX-XXXX-XXXX-XXXX","redeemInfo":{"account_id":"<Claude Organization ID>"}}'
# -> {"redemptionId":"...","status":"pending","code":"...","orderId":"..."}
# then poll: curl -H "Authorization: Bearer awk_your_key" https://aibulk.uk/api/wholesale/v1/redemptions/<redemptionId>Error codes, payload examples and retry rules are in the Full API reference.
Optional: delivery webhooks
Get order.delivered / topup.credited pushed to your server instead of polling. Configure the URL (the signing secret is returned once — reconfiguring rotates it), then use POST /wholesale/v1/webhook/test to verify:
curl -X PUT https://aibulk.uk/api/wholesale/v1/webhook \
-H "Authorization: Bearer awk_your_key" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-server.example.com/aibulk-webhook"}'Verify the HMAC signature against the raw request body before trusting a delivery:
// Node.js — verify an AIBulk webhook delivery
import { createHmac, timingSafeEqual } from "node:crypto";
function isValidAibulkWebhook(rawBody, signatureHeader, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return !!signatureHeader && signatureHeader.length === expected.length &&
timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
}
// app.post("/aibulk-webhook", ...): verify X-AIBulk-Signature against the raw body,
// dedupe on X-AIBulk-Event-Id, then respond 2xx within 5 seconds.Events: order.delivered {orderId, externalOrderId, productId, qty, codes[], redeemUrl} / topup.credited {orderId, externalTopupId, amount, currency} / redemption.fulfilled & redemption.failed {orderId, externalOrderId, productId, redemptionId, code, status, failureCode, retryable, fulfilledAt} / order.delivery_attention / webhook.test. Envelope fields eventId / event / timestamp are merged into the same JSON. Non-2xx or >5s responses are retried up to 10 times with exponential backoff (capped at 6h); redeliveries share the same X-AIBulk-Event-Id — dedupe on it. Full schemas: openapi.json → webhooks.
Conventions & limits
- Amounts are always decimal strings (e.g. "12.5"); currencies are USDT / USDC.
- Orders must carry externalOrderId; top-ups must carry externalTopupId (or Idempotency-Key). Reuse the same value on every retry. Pass expectedUnitPrice when you want a changed quote to fail with price_changed.
- Check availability before larger orders. payment=balance debits instantly; direct_code gift cards may return paid with codes=[] until ready; payment=intent returns a checkoutUrl — give it only to the payer and don't log or publish it; poll the order every 10–30s after manual payment (status sync is throttled to 10s). Unpaid intent orders expire after ~15 minutes, but a payment completed late is still honored and delivered by reconciliation.
- All-channel unit prices are fixed per user level (Lv1–Lv5), with web and API the same; level score = cumulative spending + balance, raised by topping up.
- Error responses are always
{ error, message, requestId }— branch on the error code (unauthorized / insufficient_balance / rate_limited …). - Rate limit: 300 requests/minute. Use X-RateLimit-* and Retry-After response headers for backoff; retain X-Request-Id for support.
- For deliveryType=redemption, deliver each code with redeemUrl. For direct_code, deliver actual gift card codes; redeemUrl is null. Group denominations using productGroupId, denomination, faceCurrency and region; order the selected leaf id. stockMode=limited requires a quantity check. fulfillmentStatus=needs_attention means manual delivery/refund review; keep the original order.
- Unpaid payment=intent orders and top-ups expire after about 15 minutes; create a new order with a new externalOrderId afterwards. USDC top-ups are credited 1:1 to the USDT balance.
- Codes issued through the API have no expiry date. A code is consumed the first time it is submitted; a failed redemption can be resubmitted with corrected details.