Developer·Growth+

ACOS Public API & Webhooks

Integrate ACOS into your own stack. Authenticate with scoped API keys, query the full operational loop — CRM, Projects, Time, Finance — and subscribe to real-time events via webhooks.

REST API

JSON over HTTPS

Scoped API Keys

6 permission scopes

Webhooks

14 real-time events

HMAC-SHA256

Signed payloads

Getting Started

Overview

Every ACOS REST endpoint follows the same conventions. Learn them once, use them everywhere.

Base URL

All API requests go to the same base URL. Every response is JSON.

https://api.acos.auricorium.com/api/v1/

Versioning

The current version is v1. Breaking changes will be released under a new version prefix. You will receive advance notice before any version is deprecated.

Response format

All endpoints return JSON. List endpoints return a plain array (no pagination wrapper). Errors return a consistent shape with a detail or code field.

All API responses include a X-Request-Id header. Include it when contacting support to speed up debugging.
Example response (contact)
{
  "id": "c8f1e3a2-...",
  "name": "Acme Corp",
  "email": "[email protected]",
  "type": "company",
  "organization": "org_id_here",
  "created_at": "2026-03-15T09:00:00Z"
}

Authentication

ACOS supports two auth methods. API keys are for machine-to-machine integrations; JWTs are for user sessions inside the app.

API Key

Pass your key in the X-API-Key header. Keys are scoped to specific resources and can be revoked from Settings → Developer.

Store API keys in environment variables. Never commit them to source control. Each key is shown only once — immediately after creation.

Key lifecycle

Keys can be revoked at any time. Revoking a key immediately returns 401 on all subsequent requests. Keys do not expire automatically.

cURL
curl https://api.acos.auricorium.com/api/v1/crm/contacts/ \
  -H "X-API-Key: acos_live_xxxxxxxxxxxx"
Python
import httpx

client = httpx.Client(
    base_url="https://api.acos.auricorium.com/api/v1",
    headers={"X-API-Key": "acos_live_xxxx"},
)
contacts = client.get("/crm/contacts/").json()
Node.js
const res = await fetch("https://api.acos.auricorium.com/api/v1/crm/contacts/", {
  headers: { "X-API-Key": "acos_live_xxxx" },
});
const contacts = await res.json();

First Request

Get up and running in under two minutes.

1

Create an API key

Go to Settings → Developer and click New API Key. Choose the scopes you need and copy the key — it will only be shown once.

2

Make a request

Pass the key in X-API-Key. The example on the right fetches your contacts list.

3

Check the response

You'll get a JSON array of contacts scoped to your organization.

cURL — list contacts
curl https://api.acos.auricorium.com/api/v1/crm/contacts/ \
  -H "X-API-Key: YOUR_KEY_HERE" \
  -H "Accept: application/json"

# Response
[
  {
    "id": "c8f1e3a2-...",
    "name": "Acme Corp",
    "email": "[email protected]",
    "type": "company",
    "phone": "+1 415 555 0100",
    "created_at": "2026-03-15T09:00:00Z"
  }
]
API Reference

Contacts

Companies and individual people in your CRM. Requires read:crm or write:crm.

GET
https://api.acos.auricorium.com/api/v1/crm/contacts/

List all contacts for your organization

GET
https://api.acos.auricorium.com/api/v1/crm/contacts/{id}/

Retrieve a single contact

POST
https://api.acos.auricorium.com/api/v1/crm/contacts/

Create a new contact

PATCH
https://api.acos.auricorium.com/api/v1/crm/contacts/{id}/

Update a contact

DELETE
https://api.acos.auricorium.com/api/v1/crm/contacts/{id}/

Delete a contact

Contact object

FieldTypeDescription
idUUIDUnique identifier
namestringFull name or company name
emailstringPrimary email address
typestring"person" or "company"
phonestringPhone number (optional)
companyUUIDParent company ID if type is person
created_atdatetimeISO-8601 timestamp
Create contact
curl -X POST https://api.acos.auricorium.com/api/v1/crm/contacts/ \
  -H "X-API-Key: acos_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Smith",
    "email": "[email protected]",
    "type": "person",
    "phone": "+44 7700 900000"
  }'

Deals

Sales pipeline deals. Requires read:crm or write:crm.

GET
https://api.acos.auricorium.com/api/v1/crm/deals/

List all deals

GET
https://api.acos.auricorium.com/api/v1/crm/deals/{id}/

Retrieve a single deal

POST
https://api.acos.auricorium.com/api/v1/crm/deals/

Create a deal

PATCH
https://api.acos.auricorium.com/api/v1/crm/deals/{id}/

Update a deal (status, value, stage)

DELETE
https://api.acos.auricorium.com/api/v1/crm/deals/{id}/

Delete a deal

FieldTypeDescription
idUUIDUnique identifier
titlestringDeal name
valuedecimalDeal value
currencystring3-letter ISO code (e.g. USD)
statusstring"active", "won", or "lost"
contactUUIDAssociated contact
pipeline_stageUUIDCurrent pipeline stage
close_datedateExpected close date (optional)

Projects

Billable client projects. Requires read:projects or write:projects.

GET
https://api.acos.auricorium.com/api/v1/projects/

List all projects

GET
https://api.acos.auricorium.com/api/v1/projects/{id}/

Retrieve a project (includes budget, status)

POST
https://api.acos.auricorium.com/api/v1/projects/

Create a project

PATCH
https://api.acos.auricorium.com/api/v1/projects/{id}/

Update a project

DELETE
https://api.acos.auricorium.com/api/v1/projects/{id}/

Archive a project

FieldTypeDescription
idUUIDUnique identifier
namestringProject name
statusstring"active", "on_hold", "completed", "archived"
typestring"fixed", "hourly", "retainer"
budgetdecimalBudget in organization currency
total_costdecimalTotal invoiced so far
contactUUIDLinked client contact
start_datedateProject start date
end_datedateProject deadline
Create project
curl -X POST https://api.acos.auricorium.com/api/v1/projects/ \
  -H "X-API-Key: acos_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Website Redesign",
    "type": "fixed",
    "budget": "12000.00",
    "contact": "c8f1e3a2-...",
    "start_date": "2026-07-01",
    "end_date": "2026-09-30"
  }'

Tasks

Project tasks and sub-tasks. Requires read:projects or write:projects.

GET
https://api.acos.auricorium.com/api/v1/tasks/

List tasks (filter by project, status, assignee)

GET
https://api.acos.auricorium.com/api/v1/tasks/{id}/

Retrieve a task with comments and attachments

POST
https://api.acos.auricorium.com/api/v1/tasks/

Create a task

PATCH
https://api.acos.auricorium.com/api/v1/tasks/{id}/

Update task status, assignee, or due date

DELETE
https://api.acos.auricorium.com/api/v1/tasks/{id}/

Delete a task

Query paramTypeDescription
projectUUIDFilter by project ID
statusstring"todo", "in_progress", "done", "blocked"
assigneeUUIDFilter by assigned user
prioritystring"low", "medium", "high", "urgent"

Invoices

Read invoice data for reporting and accounting sync. Requires read:finance. Write operations are not available to API keys.

GET
https://api.acos.auricorium.com/api/v1/finance/invoices/

List all invoices

GET
https://api.acos.auricorium.com/api/v1/finance/invoices/{id}/

Retrieve an invoice (includes line items and payments)

Invoice mutations (create, update, mark paid) are intentionally not exposed via the public API. Use the ACOS web app or the Xero / QuickBooks integrations to manage invoice state.

Time Entries

Push time records from external timers or CLI tools. Requires write:time.

POST
https://api.acos.auricorium.com/api/v1/time/entries/

Create a time entry

PATCH
https://api.acos.auricorium.com/api/v1/time/entries/{id}/

Correct hours or description

DELETE
https://api.acos.auricorium.com/api/v1/time/entries/{id}/

Remove a time entry

Read access to time entries is not available to API keys — reads require a user JWT session. This prevents contractors from enumerating other team members' time.
Log time
curl -X POST https://api.acos.auricorium.com/api/v1/time/entries/ \
  -H "X-API-Key: acos_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "project": "proj_uuid_here",
    "task": "task_uuid_here",
    "hours": 2.5,
    "date": "2026-06-30",
    "description": "API integration work"
  }'
Webhooks

Webhooks — Setup

Subscribe to events and receive real-time HTTP POST requests to your endpoint when things happen in ACOS.

Creating a webhook

Register an endpoint in Settings → Developer → Webhooks. You choose which events to subscribe to and ACOS generates a signing secret.

Delivery

ACOS delivers each event via POST with a Content-Type: application/json body. Your endpoint must return 2xx within 10 seconds. Failed deliveries are retried up to 3 times with exponential backoff (30 s → 5 min → 30 min).

Retry behaviour

AttemptDelay after failure
1st retry30 seconds
2nd retry5 minutes
3rd retry30 minutes
Give upMarked failed in delivery log
Minimal webhook receiver (Node.js)
import crypto from "node:crypto";
import express from "express";

const app = express();
app.use(express.json());

const SECRET = process.env.ACOS_WEBHOOK_SECRET;

app.post("/webhooks/acos", (req, res) => {
  const sig  = req.headers["x-acos-signature"];
  const body = JSON.stringify(req.body);

  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(body)
    .digest("hex");

  if (sig !== `sha256=${expected}`) {
    return res.status(401).send("Bad signature");
  }

  const { event, data } = req.body;
  console.log("Received:", event, data);

  res.sendStatus(200);
});

app.listen(3000);

Event Catalogue

14 events span the full ACOS operational loop.

CRM
contact.created

A new contact was added

contact.updated

A contact's fields changed

deal.created

A deal entered the pipeline

deal.updated

A deal's stage or value changed

deal.won

A deal was marked won

deal.lost

A deal was marked lost

Projects & Tasks
project.created

A new project was created

project.completed

Project status changed to completed

task.created

A new task was added

task.completed

A task was marked done

Finance
invoice.created

An invoice was drafted

invoice.sent

An invoice was emailed to the client

invoice.paid

An invoice was marked paid

invoice.overdue

An invoice crossed its due date unpaid

Payload & Signature Verification

Every delivery carries a consistent envelope and an HMAC-SHA256 signature.

Envelope

Every webhook body wraps the event data in a consistent envelope. The data object is the full serialized resource at the time the event fired.

Verifying the signature

ACOS signs the raw request body with HMAC-SHA256 using your webhook secret and includes the result in the X-ACOS-Signature header as sha256=hex_digest. Always verify before processing.

Never trust the payload without verifying the signature. A failing signature check should return 401 immediately.

Python verification

Python
import hashlib, hmac

def verify(secret: str, body: bytes, sig_header: str) -> bool:
    digest = hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    expected = f"sha256={digest}"
    return hmac.compare_digest(expected, sig_header)
Webhook payload envelope
{
  "event":      "deal.won",
  "organization": "org_uuid_here",
  "timestamp":  "2026-06-30T12:34:56Z",
  "data": {
    "id":     "deal_uuid",
    "title":  "Acme Corp Website",
    "value":  "12000.00",
    "status": "won",
    "contact": {
      "id":   "contact_uuid",
      "name": "Acme Corp"
    }
  }
}

# Request headers
X-ACOS-Signature: sha256=a1b2c3d4...
X-Request-Id: req_abc123
Content-Type: application/json
Reference

Scopes

API keys are least-privilege by design. Grant only what your integration needs.

ScopePlanReadWriteCovers
read:crmGrowth+Contacts, Deals, Proposals
write:crmScaleCreate / update / delete Contacts and Deals
read:projectsGrowth+Projects, Tasks
write:projectsScaleCreate / update / delete Projects and Tasks
read:financeGrowth+Invoices, Expenses (read-only)
write:timeGrowth+Create / update Time Entries
Growth plan keys are restricted to read scopes only. Scale plan keys unlock write scopes. You can create multiple keys with different scope combinations.

Rate Limits

Limits are per organization per day and reset at midnight UTC.

PlanDaily API requestsBurst (per minute)
Growth+1,000 / day60 / min
Scale10,000 / day300 / min

When you exceed the rate limit, ACOS returns 429 Too Many Requests with a Retry-After header indicating seconds until the next window.

429 response
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1751244000

{
  "detail": "Rate limit exceeded.",
  "code": "rate_limited"
}

Error Codes

All errors return a consistent JSON body with a detail message and optional code field.

HTTP StatuscodeMeaning
400validation_errorRequest body failed validation — check the fields object for details
401not_authenticatedMissing or invalid API key
403insufficient_scopeYour key lacks the required scope for this endpoint
403plan_requiredFeature requires a higher subscription plan
404not_foundResource does not exist or belongs to another org
409conflictResource already exists (e.g. duplicate invoice number)
422unprocessableRequest was understood but semantically invalid
429rate_limitedDaily or per-minute rate limit exceeded
500server_errorUnexpected server error — include X-Request-Id when reporting

Ready to integrate?

Create your first API key from Settings → Developer. Requires Growth plan or above.