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
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.
X-Request-Id header. Include it when contacting support to speed up debugging.{
"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.
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 https://api.acos.auricorium.com/api/v1/crm/contacts/ \ -H "X-API-Key: acos_live_xxxxxxxxxxxx"
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()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.
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.
Make a request
Pass the key in X-API-Key. The example on the right fetches your contacts list.
Check the response
You'll get a JSON array of contacts scoped to your organization.
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"
}
]Contacts
Companies and individual people in your CRM. Requires read:crm or write:crm.
https://api.acos.auricorium.com/api/v1/crm/contacts/List all contacts for your organization
https://api.acos.auricorium.com/api/v1/crm/contacts/{id}/Retrieve a single contact
https://api.acos.auricorium.com/api/v1/crm/contacts/Create a new contact
https://api.acos.auricorium.com/api/v1/crm/contacts/{id}/Update a contact
https://api.acos.auricorium.com/api/v1/crm/contacts/{id}/Delete a contact
Contact object
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique identifier |
| name | string | Full name or company name |
| string | Primary email address | |
| type | string | "person" or "company" |
| phone | string | Phone number (optional) |
| company | UUID | Parent company ID if type is person |
| created_at | datetime | ISO-8601 timestamp |
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.
https://api.acos.auricorium.com/api/v1/crm/deals/List all deals
https://api.acos.auricorium.com/api/v1/crm/deals/{id}/Retrieve a single deal
https://api.acos.auricorium.com/api/v1/crm/deals/Create a deal
https://api.acos.auricorium.com/api/v1/crm/deals/{id}/Update a deal (status, value, stage)
https://api.acos.auricorium.com/api/v1/crm/deals/{id}/Delete a deal
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique identifier |
| title | string | Deal name |
| value | decimal | Deal value |
| currency | string | 3-letter ISO code (e.g. USD) |
| status | string | "active", "won", or "lost" |
| contact | UUID | Associated contact |
| pipeline_stage | UUID | Current pipeline stage |
| close_date | date | Expected close date (optional) |
Projects
Billable client projects. Requires read:projects or write:projects.
https://api.acos.auricorium.com/api/v1/projects/List all projects
https://api.acos.auricorium.com/api/v1/projects/{id}/Retrieve a project (includes budget, status)
https://api.acos.auricorium.com/api/v1/projects/Create a project
https://api.acos.auricorium.com/api/v1/projects/{id}/Update a project
https://api.acos.auricorium.com/api/v1/projects/{id}/Archive a project
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique identifier |
| name | string | Project name |
| status | string | "active", "on_hold", "completed", "archived" |
| type | string | "fixed", "hourly", "retainer" |
| budget | decimal | Budget in organization currency |
| total_cost | decimal | Total invoiced so far |
| contact | UUID | Linked client contact |
| start_date | date | Project start date |
| end_date | date | Project deadline |
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.
https://api.acos.auricorium.com/api/v1/tasks/List tasks (filter by project, status, assignee)
https://api.acos.auricorium.com/api/v1/tasks/{id}/Retrieve a task with comments and attachments
https://api.acos.auricorium.com/api/v1/tasks/Create a task
https://api.acos.auricorium.com/api/v1/tasks/{id}/Update task status, assignee, or due date
https://api.acos.auricorium.com/api/v1/tasks/{id}/Delete a task
| Query param | Type | Description |
|---|---|---|
| project | UUID | Filter by project ID |
| status | string | "todo", "in_progress", "done", "blocked" |
| assignee | UUID | Filter by assigned user |
| priority | string | "low", "medium", "high", "urgent" |
Invoices
Read invoice data for reporting and accounting sync. Requires read:finance. Write operations are not available to API keys.
https://api.acos.auricorium.com/api/v1/finance/invoices/List all invoices
https://api.acos.auricorium.com/api/v1/finance/invoices/{id}/Retrieve an invoice (includes line items and payments)
Time Entries
Push time records from external timers or CLI tools. Requires write:time.
https://api.acos.auricorium.com/api/v1/time/entries/Create a time entry
https://api.acos.auricorium.com/api/v1/time/entries/{id}/Correct hours or description
https://api.acos.auricorium.com/api/v1/time/entries/{id}/Remove a time entry
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 — 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
| Attempt | Delay after failure |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| Give up | Marked failed in delivery log |
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.
contact.createdA new contact was added
contact.updatedA contact's fields changed
deal.createdA deal entered the pipeline
deal.updatedA deal's stage or value changed
deal.wonA deal was marked won
deal.lostA deal was marked lost
project.createdA new project was created
project.completedProject status changed to completed
task.createdA new task was added
task.completedA task was marked done
invoice.createdAn invoice was drafted
invoice.sentAn invoice was emailed to the client
invoice.paidAn invoice was marked paid
invoice.overdueAn 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.
401 immediately.Python verification
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){
"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/jsonScopes
API keys are least-privilege by design. Grant only what your integration needs.
| Scope | Plan | Read | Write | Covers |
|---|---|---|---|---|
read:crm | Growth+ | ✓ | — | Contacts, Deals, Proposals |
write:crm | Scale | — | ✓ | Create / update / delete Contacts and Deals |
read:projects | Growth+ | ✓ | — | Projects, Tasks |
write:projects | Scale | — | ✓ | Create / update / delete Projects and Tasks |
read:finance | Growth+ | ✓ | — | Invoices, Expenses (read-only) |
write:time | Growth+ | — | ✓ | Create / update Time Entries |
Rate Limits
Limits are per organization per day and reset at midnight UTC.
| Plan | Daily API requests | Burst (per minute) |
|---|---|---|
| Growth+ | 1,000 / day | 60 / min |
| Scale | 10,000 / day | 300 / min |
When you exceed the rate limit, ACOS returns 429 Too Many Requests with a Retry-After header indicating seconds until the next window.
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 Status | code | Meaning |
|---|---|---|
| 400 | validation_error | Request body failed validation — check the fields object for details |
| 401 | not_authenticated | Missing or invalid API key |
| 403 | insufficient_scope | Your key lacks the required scope for this endpoint |
| 403 | plan_required | Feature requires a higher subscription plan |
| 404 | not_found | Resource does not exist or belongs to another org |
| 409 | conflict | Resource already exists (e.g. duplicate invoice number) |
| 422 | unprocessable | Request was understood but semantically invalid |
| 429 | rate_limited | Daily or per-minute rate limit exceeded |
| 500 | server_error | Unexpected server error — include X-Request-Id when reporting |
Ready to integrate?
Create your first API key from Settings → Developer. Requires Growth plan or above.