API reference
REST API over HTTPS. JSON in, JSON out. Every response includes a request_id for support.
https://api.aceralabs.com.au/v1Versionv1stable · released 2026-04-01Authentication
All endpoints require a Bearer token. Tokens are workspace-scoped and do not expire unless revoked or given an explicit expiry date at creation.
Getting a token
- Open Settings, then API.
- Click Create token.
- Set a name and an optional expiry date. The token value is shown once on creation. Copy it immediately.
Using a token
Include the token in the Authorization header on every request.
curl https://api.aceralabs.com.au/v1/queue \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json"If the token is missing or invalid, the API returns 401 Unauthorized. If the token lacks permission for the requested operation, it returns 403 Forbidden.
Rate limits
| Tier | Requests per minute | Burst |
|---|---|---|
| Trial | 20 | 40 |
| Growth | 60 | 120 |
| Pro | 300 | 600 |
| Enterprise | 1,000 | 2,000 |
Rate limit headers are included on every response:
X-RateLimit-Limit: requests allowed per minuteX-RateLimit-Remaining: requests remaining in the current windowX-RateLimit-Reset: Unix timestamp when the window resets
When you exceed the rate limit, the API returns 429 Too Many Requests. Wait until X-RateLimit-Reset and retry.
Endpoints
GET /queue
/queueReturns today’s queue for the authenticated workspace.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
tier | string | No | Filter by urgency tier: urgent, today, handled. Omit for all. |
date | string | No | ISO 8601 date. Returns the queue for that date. Defaults to today. |
limit | integer | No | Number of packets to return. Default 50, max 200. |
offset | integer | No | Pagination offset. Default 0. |
Example request
curl "https://api.aceralabs.com.au/v1/queue?tier=urgent" \
-H "Authorization: Bearer YOUR_TOKEN"Example response
{
"date": "2026-05-12",
"total": 3,
"urgent": 1,
"today": 2,
"handled": 14,
"packets": [
{
"id": "pkt_01hwxyz123",
"tier": "urgent",
"agent": "campaign_agent",
"title": "Reallocate $14,200 from Display to Search this week",
"summary": "Search is saturating below CPA target with $4.2k of unspent headroom.",
"at_stake_value": 14200,
"projected_lift": "+1.4 ROAS",
"time_to_realise_days": 9,
"mmm_backed": true,
"confidence": "high",
"created_at": "2026-05-12T07:00:00+10:00",
"status": "pending"
}
],
"request_id": "req_01hwxyz456"
}GET /packets/:id
/packets/:idReturns a specific decision packet by ID, including the full evidence panel.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Packet ID, e.g. pkt_01hwxyz123 |
Example request
curl "https://api.aceralabs.com.au/v1/packets/pkt_01hwxyz123" \
-H "Authorization: Bearer YOUR_TOKEN"Example response
{
"id": "pkt_01hwxyz123",
"tier": "urgent",
"agent": "campaign_agent",
"title": "Reallocate $14,200 from Display to Search this week",
"evidence": {
"mmm_backed": true,
"model_source": "recast",
"model_run_date": "2026-05-05",
"channels": [
{ "name": "search", "saturation_status": "below", "roi_index": 0.62 },
{ "name": "display", "saturation_status": "above", "roi_index": 0.94 }
]
},
"campaign_memory": {
"reference_decision_date": "2025-11-03",
"description": "Same move on AW Q3 flight. Search ROAS lifted from 3.2 to 4.6 in 9 days."
},
"status": "pending",
"created_at": "2026-05-12T07:00:00+10:00",
"request_id": "req_01hwxyz789"
}POST /packets/:id/approve
/packets/:id/approveApproves a pending packet and triggers the write connector to push the change to the connected platform.
Request body
| Name | Type | Required | Description |
|---|---|---|---|
note | string | No | Internal note stored with the approval record. |
Example request
curl -X POST "https://api.aceralabs.com.au/v1/packets/pkt_01hwxyz123/approve" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"note": "Approved in weekly review"}'Example response
{
"id": "pkt_01hwxyz123",
"status": "approved",
"approved_at": "2026-05-12T09:14:22+10:00",
"approved_by": "user_01hwxyz",
"push_status": "pushing",
"push_estimated_completion_seconds": 5,
"request_id": "req_01hwxyz012"
}Push status progresses from pushing to pushed when the connector confirms. Poll GET /packets/:id or subscribe to the packet.activated webhook for the final status.
POST /packets/:id/edit
/packets/:id/editEdits the recommended values in a pending packet before approving it. After editing, the packet status remains pending. Submit a separate POST /packets/:id/approve to approve.
Request body
| Name | Type | Required | Description |
|---|---|---|---|
channel_overrides | array | No | Array of channel-level spend override objects. Each object requires channel (string) and spend_delta (integer, positive or negative dollars). |
note | string | No | Internal note for the audit record. |
Example request
curl -X POST "https://api.aceralabs.com.au/v1/packets/pkt_01hwxyz123/edit" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel_overrides": [
{ "channel": "search", "spend_delta": 10000 },
{ "channel": "display", "spend_delta": -10000 }
],
"note": "Reducing reallocation to $10k vs $14.2k recommended"
}'Example response
{
"id": "pkt_01hwxyz123",
"status": "pending",
"edited_at": "2026-05-12T09:12:05+10:00",
"edited_by": "user_01hwxyz",
"channel_overrides_applied": 2,
"request_id": "req_01hwxyz055"
}POST /packets/:id/dismiss
/packets/:id/dismissDismisses a pending packet. The agent records the dismissal and adjusts its threshold for similar signals if the same type is dismissed three consecutive times.
Request body
| Name | Type | Required | Description |
|---|---|---|---|
reason | string | No | Reason for dismissal. Stored in audit log and used by the agent to calibrate future recommendations. |
Example request
curl -X POST "https://api.aceralabs.com.au/v1/packets/pkt_01hwxyz123/dismiss" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason": "Campaign is under a fixed spend commitment this month"}'Example response
{
"id": "pkt_01hwxyz123",
"status": "dismissed",
"dismissed_at": "2026-05-12T09:15:44+10:00",
"dismissed_by": "user_01hwxyz",
"request_id": "req_01hwxyz066"
}GET /connectors
/connectorsReturns all connectors configured for the authenticated workspace.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter by status: connected, disconnected, error. Omit for all. |
Example request
curl "https://api.aceralabs.com.au/v1/connectors" \
-H "Authorization: Bearer YOUR_TOKEN"Example response
{
"connectors": [
{
"id": "conn_01hwxyz",
"platform": "google_ads",
"type": "read_write_planning",
"status": "connected",
"last_sync": "2026-05-12T02:45:00+10:00",
"accounts_in_scope": 2
}
],
"total": 1,
"request_id": "req_01hwxyz345"
}GET /agents
/agentsReturns the status of all six agents for the most recent overnight cycle.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
date | string | No | ISO 8601 date. Returns agent status for that cycle date. Defaults to most recent. |
Example request
curl "https://api.aceralabs.com.au/v1/agents" \
-H "Authorization: Bearer YOUR_TOKEN"GET /workspaces/me
/workspaces/meReturns the authenticated workspace’s configuration.
Example request
curl "https://api.aceralabs.com.au/v1/workspaces/me" \
-H "Authorization: Bearer YOUR_TOKEN"Webhooks
Subscribe to packet lifecycle events via webhooks. All outbound webhook requests are signed with HMAC-SHA256 using your webhook secret.
Available events
| Event | Triggered when |
|---|---|
packet.created | A new packet is added to the queue (at 07:00 each morning) |
packet.approved | A packet is approved via the UI or API |
packet.activated | The write connector confirms the change is live on the platform |
packet.dismissed | A packet is dismissed |
packet.push_failed | The write connector failed to push after three retries |
Webhook payload shape
{
"event": "packet.approved",
"workspace_id": "ws_01hwxyz",
"timestamp": "2026-05-12T09:14:22+10:00",
"data": {
"id": "pkt_01hwxyz123",
"status": "approved",
"approved_by": "user_01hwxyz",
"approved_at": "2026-05-12T09:14:22+10:00"
}
}Verifying signatures
Every webhook request includes an X-Acera-Signature header. Compute the expected signature and compare.
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expected}` === signature;
}Errors
All errors return a JSON body with error, message, and request_id fields.
| HTTP status | Error code | Description |
|---|---|---|
| 400 | bad_request | Missing required field or invalid field value |
| 401 | unauthorized | Missing or invalid Bearer token |
| 403 | forbidden | Token lacks permission for this operation |
| 404 | not_found | Packet, connector, or resource not found |
| 409 | conflict | Operation conflicts with current resource state |
| 422 | unprocessable | Request valid but cannot be completed |
| 429 | rate_limited | Rate limit exceeded. See X-RateLimit-Reset header. |
| 500 | internal_error | Server error. Include request_id when contacting support. |
SDKs
Official SDKs are planned for Q3 2026. Until then, use the REST API directly.
- TypeScript (Node): coming Q3 2026
- Python: coming Q3 2026
Community SDK contributions are welcome via GitHub.
Changelog
| Date | Version | Change |
|---|---|---|
| 2026-05-09 | v1 | GET /plans AdCP endpoint added, beta |
| 2026-05-02 | v1 | Webhook signatures mandatory (HMAC-SHA256) |
| 2026-04-24 | v1 | Microsoft Ads connector generally available |
| 2026-04-17 | v1 | POST /packets/:id/edit endpoint added |
| 2026-04-01 | v1 | v1 stable release; v0.1.x deprecated |
Concepts
The queue and decision packet structure that these endpoints return.
Security
API token scoping, expiry, and revocation policy.
Connectors
GET /connectors returns your connector configuration.
Guides
API-first guides: export the audit trail, read the queue from your CRM.