Acera Labs
// Build

API reference

REST API over HTTPS. JSON in, JSON out. Every response includes a request_id for support.

Base URLhttps://api.aceralabs.com.au/v1Versionv1stable · released 2026-04-01

Authentication

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

  1. Open Settings, then API.
  2. Click Create token.
  3. 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

TierRequests per minuteBurst
Trial2040
Growth60120
Pro300600
Enterprise1,0002,000

Rate limit headers are included on every response:

  • X-RateLimit-Limit: requests allowed per minute
  • X-RateLimit-Remaining: requests remaining in the current window
  • X-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

GET/queue

Returns today’s queue for the authenticated workspace.

Parameters

NameTypeRequiredDescription
tierstringNoFilter by urgency tier: urgent, today, handled. Omit for all.
datestringNoISO 8601 date. Returns the queue for that date. Defaults to today.
limitintegerNoNumber of packets to return. Default 50, max 200.
offsetintegerNoPagination offset. Default 0.

Example request

curl "https://api.aceralabs.com.au/v1/queue?tier=urgent" \ -H "Authorization: Bearer YOUR_TOKEN"

Example response

json
{
  "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

GET/packets/:id

Returns a specific decision packet by ID, including the full evidence panel.

Path parameters

NameTypeRequiredDescription
idstringYesPacket ID, e.g. pkt_01hwxyz123

Example request

curl "https://api.aceralabs.com.au/v1/packets/pkt_01hwxyz123" \ -H "Authorization: Bearer YOUR_TOKEN"

Example response

json
{
  "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

POST/packets/:id/approve

Approves a pending packet and triggers the write connector to push the change to the connected platform.

Request body

NameTypeRequiredDescription
notestringNoInternal 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

json
{
  "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

POST/packets/:id/edit

Edits 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

NameTypeRequiredDescription
channel_overridesarrayNoArray of channel-level spend override objects. Each object requires channel (string) and spend_delta (integer, positive or negative dollars).
notestringNoInternal 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

json
{
  "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

POST/packets/:id/dismiss

Dismisses 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

NameTypeRequiredDescription
reasonstringNoReason 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

json
{
  "id": "pkt_01hwxyz123",
  "status": "dismissed",
  "dismissed_at": "2026-05-12T09:15:44+10:00",
  "dismissed_by": "user_01hwxyz",
  "request_id": "req_01hwxyz066"
}

GET /connectors

GET/connectors

Returns all connectors configured for the authenticated workspace.

Parameters

NameTypeRequiredDescription
statusstringNoFilter 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

json
{
  "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

GET/agents

Returns the status of all six agents for the most recent overnight cycle.

Parameters

NameTypeRequiredDescription
datestringNoISO 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

GET/workspaces/me

Returns 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

EventTriggered when
packet.createdA new packet is added to the queue (at 07:00 each morning)
packet.approvedA packet is approved via the UI or API
packet.activatedThe write connector confirms the change is live on the platform
packet.dismissedA packet is dismissed
packet.push_failedThe write connector failed to push after three retries

Webhook payload shape

json
{
  "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 statusError codeDescription
400bad_requestMissing required field or invalid field value
401unauthorizedMissing or invalid Bearer token
403forbiddenToken lacks permission for this operation
404not_foundPacket, connector, or resource not found
409conflictOperation conflicts with current resource state
422unprocessableRequest valid but cannot be completed
429rate_limitedRate limit exceeded. See X-RateLimit-Reset header.
500internal_errorServer 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

DateVersionChange
2026-05-09v1GET /plans AdCP endpoint added, beta
2026-05-02v1Webhook signatures mandatory (HMAC-SHA256)
2026-04-24v1Microsoft Ads connector generally available
2026-04-17v1POST /packets/:id/edit endpoint added
2026-04-01v1v1 stable release; v0.1.x deprecated