API reference
The AOA API sandbox: the same endpoints on test data, a test key with no sign-up, test payments and webhooks with no real bookings or charges.
The sandbox is a copy of the public API on made-up data: https://aoa.com.ua/api/sandbox/v1. The same paths, parameters, response format, error codes and rate limit headers as /api/v1, so an integration tested here moves to the live API by swapping the base URL and the key.
Bookings, ticket holds and payments go all the way through their statuses, but with no side effects: no database records, emails, venue notifications or charges. Sandbox state lives for one day.
Reading works without a key, just like in the live API. Writing needs a test key, and nobody has to issue it: it’s any string with the aoa_test_ prefix and at least 16 random characters after it. The key only separates your test orders and webhooks from everyone else’s, so make it random. The sandbox rejects live aoa_live_ keys with 401.
# Make up the test key yourself: the prefix plus at least 16 random characters
export AOA_API_KEY="aoa_test_$(openssl rand -hex 16)"| Id | What it is | What it tests |
|---|---|---|
sbx-cafe | A café with table booking, up to 8 guests, closed on Mondays | Time slots, a table booking, reason: "closed" on Monday |
sbx-bar | A bar without online booking | reason: "disabled", a refused booking |
sbx-lecture | A free lecture | checkout for free tickets returns 400 |
sbx-concert | A concert: sbx-concert-standard on sale, sbx-concert-vip sold out, sbx-concert-early with sales ended | Payment, 409 on a sold-out ticket, 400 when sales have ended |
sbx-workshop | A workshop with sales not started yet | status: "sales_not_started" and 400 on checkout |
Ticket stock in the sandbox is fixed: every integration shares it, so someone else’s hold mustn’t sell out the tickets your test needs.
paymentUrl from checkout leads to a test payment page with “Pay” and “Decline” buttons. An agent can also finish the payment on its own, without a person, with an endpoint the live API doesn’t have:
/orders/{paymentId}/simulate# 1. Sandbox events (no key needed)
curl "https://aoa.com.ua/api/sandbox/v1/events"
# 2. Pay for a ticket: the same request as against the live API
curl -X POST "https://aoa.com.ua/api/sandbox/v1/checkout" \
-H "Authorization: Bearer $AOA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"eventId": "sbx-concert",
"tickets": [{"ticketTypeId": "sbx-concert-standard", "quantity": 2}],
"buyer": {"email": "test@example.com", "name": "Test Buyer"}}'
# 3. Finish the payment without a person: "paid" or "failed"
curl -X POST "https://aoa.com.ua/api/sandbox/v1/orders/<paymentId>/simulate" \
-H "Content-Type: application/json" -d '{"outcome": "paid"}'
# 4. Status, just like in the live API
curl "https://aoa.com.ua/api/sandbox/v1/orders/<paymentId>" -H "Authorization: Bearer $AOA_API_KEY"An unpaid order becomes EXPIRED after 15 minutes, just like a real one. Prefer: respond-async returns 202 with a Location pointing to the sandbox status.
Endpoints are created, updated and deleted with the same requests as in the live API. There’s one difference: the sandbox doesn’t send requests to your URL. Every event (a payment, a decline and a test event of any type) is stored as a ready-made request, and you replay it on your server. The AOA-Signature is computed with the endpoint’s secret at the moment you read it, so the 5-minute window check passes, and the same code then works unchanged in production. Fields of every event are described in Webhook events.
| Endpoint | What it does |
|---|---|
GET/webhooks/{endpointId}/deliveries | The last 20 deliveries with body and headers |
POST/webhooks/{endpointId}/test | A test event of the chosen type with sample data |
# Endpoint: the sandbox remembers it but never sends anything to it
curl -X POST "https://aoa.com.ua/api/sandbox/v1/webhooks" \
-H "Authorization: Bearer $AOA_API_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/hooks/aoa", "events": ["order.paid", "event.cancelled"]}'
# A test event of any type the endpoint is subscribed to
curl -X POST "https://aoa.com.ua/api/sandbox/v1/webhooks/<endpointId>/test" \
-H "Authorization: Bearer $AOA_API_KEY" -H "Content-Type: application/json" \
-d '{"type": "event.cancelled"}'
# Deliveries: body, AOA-Signature and the other headers exactly as in production
curl "https://aoa.com.ua/api/sandbox/v1/webhooks/<endpointId>/deliveries" -H "Authorization: Bearer $AOA_API_KEY"The official SDKs work with the sandbox with no code changes: all you need is the sandbox base URL and a test key in AOA_API_KEY.
// TypeScript
const aoa = createAoaClient({ baseUrl: 'https://aoa.com.ua/api/sandbox/v1' });
# Python
client = AoaClient(base_url="https://aoa.com.ua/api/sandbox/v1")
// Go
client := aoa.NewClient(aoa.WithBaseURL("https://aoa.com.ua/api/sandbox/v1"))60 read requests and 30 write requests per minute from one IP address, in the same RateLimit headers. The limit is counted per address, not per key: keys are free, and a new key for every request shouldn’t get around the limit. Every sandbox response carries the AOA-Environment: sandbox header.