DeveloperGrowth+ Plan

Public API & Webhooks

Connect ACOS to Zapier, Make, and your own backend using API keys and real-time webhook events. Available on Growth and above.

Overview

The ACOS public API lets you read and write data programmatically using standard REST conventions. All endpoints live at https://api.acos.auricorium.com/api/v1/ and accept application/json.

Webhooks let ACOS push events to your server the moment something happens — a deal is won, an invoice is paid, a task is completed. You subscribe to exactly the events you care about per webhook endpoint.

API keys and webhooks are available on the Growth plan and above. Growth plan keys are limited to read-only scopes. Write scopes and higher rate limits require the Scale plan.

Authentication

Pass your API key in the X-API-Key header on every request. Keys are created in Settings → API & Webhooks and shown exactly once at creation — store them in a secret manager immediately.

GET /api/v1/crm/contacts/
Host: api.acos.auricorium.com
X-API-Key: acos_myorg_a1b2c3d4e5f6a1b2c3d4e5f6

Never expose API keys in client-side code or version control. Treat them like passwords. Rotate them immediately if compromised via Settings → API & Webhooks → Revoke.

Key format

Every key follows the pattern acos_{org_slug}_{ 24 hex chars}. The first 16 characters are the key prefix shown in the dashboard — useful for identifying which key made a request.

Rate limits

PlanRequests per dayScope access
Growth1,000Read-only scopes
Scale10,000Read + write scopes
Enterprise10,000Read + write scopes

When you exceed the daily limit, the API returns 429 Too Many Requests. The counter resets at midnight UTC.

Scopes

Each API key is issued with a set of scopes that control which endpoints it can access. You choose scopes at key creation time — a key cannot access anything outside its declared scopes.

ScopeWhat it allowsPlan required
read:crmRead contacts, deals, proposals, activitiesGrowth+
write:crmCreate and update contacts and dealsScale+
read:projectsRead projects, tasks, team membersGrowth+
write:projectsCreate and update projects and tasksScale+
read:financeRead invoices, expenses, retainersGrowth+
write:timeCreate time entriesScale+

API Endpoints

The public API exposes the same endpoints your ACOS dashboard uses. All responses are JSON. List endpoints return plain arrays (no pagination wrapper). All data is scoped to your organization automatically.

CRM

MethodPathScope
GET/api/v1/crm/contacts/read:crm
GET/api/v1/crm/contacts/{id}/read:crm
POST/api/v1/crm/contacts/write:crm
GET/api/v1/crm/deals/read:crm
GET/api/v1/crm/deals/{id}/read:crm
POST/api/v1/crm/deals/write:crm
PATCH/api/v1/crm/deals/{id}/write:crm

Projects & Tasks

MethodPathScope
GET/api/v1/projects/read:projects
GET/api/v1/projects/{id}/read:projects
POST/api/v1/projects/write:projects
GET/api/v1/tasks/read:projects
POST/api/v1/tasks/write:projects
PATCH/api/v1/tasks/{id}/write:projects

Finance

MethodPathScope
GET/api/v1/finance/invoices/read:finance
GET/api/v1/finance/invoices/{id}/read:finance
GET/api/v1/finance/expenses/read:finance

Time

MethodPathScope
POST/api/v1/time/entries/write:time

Example: create a contact

curl -X POST https://api.acos.auricorium.com/api/v1/crm/contacts/ \
  -H "X-API-Key: acos_myorg_a1b2c3d4e5f6a1b2c3d4e5f6" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sarah Ahmed",
    "email": "[email protected]",
    "company": "Acme Corp"
  }'

Webhooks

Webhooks deliver a signed JSON POST request to your server URL every time a subscribed event occurs in ACOS. You can have multiple webhooks, each subscribed to a different set of events.

Setting up a webhook

  • Go to Settings → API & Webhooks
  • Click Add webhook
  • Enter your HTTPS endpoint URL
  • Select the events you want to receive
  • Click Create webhook — your signing secret is shown immediately

Webhook endpoints must use HTTPS. HTTP endpoints are rejected at creation time. Your server must respond with a 2xx status code within 10 seconds or the delivery is marked as failed.

Event catalogue

EventFires when
deal.createdA new deal is added to the pipeline
deal.stage_changedA deal moves to a different pipeline stage
deal.wonA deal is marked as Won
deal.lostA deal is marked as Lost
proposal.sentA proposal is sent to a client
proposal.acceptedA client accepts a proposal
project.createdA new project is created
project.status_changedA project status changes
task.completedA task is moved to Done
invoice.createdAn invoice is created
invoice.sentAn invoice is sent to a client
invoice.paidAn invoice is fully paid
invoice.overdueAn invoice passes its due date unpaid
time_entry.createdA time entry is logged

Payload structure

Every webhook POST body follows the same envelope:

{
  "event": "invoice.paid",
  "organization_id": "a1b2c3d4-...",
  "timestamp": "2026-06-30T14:22:01Z",
  "data": {
    "id": "inv_...",
    "number": "INV-0042",
    "total": "4500.00",
    "currency": "USD"
    // full object varies by event type
  }
}

Verifying signatures

Every request includes an X-ACOS-Signature header with an HMAC-SHA256 signature of the raw request body using your webhook secret. Always verify this before processing the payload.

import hmac
import hashlib

def verify_webhook(secret: str, payload: bytes, header: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)

# Flask example
@app.route("/webhooks/acos", methods=["POST"])
def handle():
    sig = request.headers.get("X-ACOS-Signature", "")
    if not verify_webhook(WEBHOOK_SECRET, request.data, sig):
        return "", 401
    event = request.json
    # process event["event"] ...
    return "", 200
// Node.js example
const crypto = require("crypto");

function verifyWebhook(secret, rawBody, header) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(header)
  );
}

app.post("/webhooks/acos", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.headers["x-acos-signature"] ?? "";
  if (!verifyWebhook(process.env.WEBHOOK_SECRET, req.body, sig)) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body);
  // process event.event ...
  res.sendStatus(200);
});

Retries

If your endpoint does not return a 2xx within 10 seconds, ACOS retries the delivery up to 3 times with exponential backoff: 1 minute, 5 minutes, then 30 minutes. After 3 failures the delivery is marked as failed — check the delivery log in Settings → API & Webhooks to inspect the response body.

Testing

Click the Play button next to any webhook in Settings → API & Webhooks to fire a test event. The delivery log below each webhook row shows all recent attempts with HTTP status codes and response bodies.

Managing Keys & Webhooks

Everything is managed in Settings → API & Webhooks. You must be an Owner to access this section.

  • Create multiple keys with different scope sets — e.g. one read-only key for Zapier, one write key for your backend sync
  • Revoke a key instantly; integrations using it stop working immediately
  • Enable or disable individual webhooks without deleting them
  • View the last 100 delivery attempts per webhook with full response bodies

Full API keys are shown exactly once at creation. If you lose a key, revoke it and create a new one — there is no way to retrieve a key after the creation dialog is closed.

FAQ

Can I use the API without creating an API key?

No. The public API requires an X-API-Key header on every request. JWT session tokens from the web app are not accepted on public API endpoints.

Why can't I create write-scope keys on the Growth plan?

Write access carries a higher risk profile. To keep Growth tier safe for smaller teams, write scopes are gated to Scale and above. Read-only keys on Growth cover most integration use cases (Zapier triggers, dashboards, reporting).

Do webhooks fire for events caused by API key requests?

Yes. If your API key creates a deal, the deal.created webhook fires just as it would from a human action in the dashboard.

What happens if my webhook endpoint is down for an extended period?

Deliveries are retried 3 times over ~36 minutes. After that, they are marked as failed and no further retries occur. There is no dead-letter queue — if you need guaranteed delivery, build idempotent reconciliation using the read API.

Is there an OpenAPI / Swagger spec?

A machine-readable OpenAPI 3.1 spec is on our roadmap. For now, this documentation and the endpoint tables above are the canonical reference.