Arka API
Connect your own software to Arka. Read your projects, tasks, risks and invoices; push costs and cash movements straight from your accounting system; get notified the moment things change. This is a REST API that returns JSON. Everything below works today.
OpenAPI 3.1 specification
Point Postman, Insomnia, an SDK generator or an AI agent straight at it. Served without a key. A schema is not a secret.
# generate a typed client in any language
npx @openapitools/openapi-generator-cli generate \
-i https://helios-app-mu.vercel.app/api/v1/openapi.json \
-g typescript-fetch -o ./arka-clientThe spec fills in its own server URL from whichever origin you fetch it from, so it works unchanged against any Arka deployment.
Authentication
Every request is authenticated with an API key. Create one in Arka under Settings → API Keys (workspace admins only). Keys look like ark_live_ followed by 48 hex characters. Send the key in the Authorization header on every request:
curl https://helios-app-mu.vercel.app/api/v1/projects \
-H "Authorization: Bearer ark_live_your_key_here"A key is shown once, at creation. Arka stores only a SHA-256 hash of it, so it can never be displayed again: if you lose it, revoke it and create a new one. Keys act on behalf of your whole organisation and can never reach another organisation's data. A revoked key stops working immediately.
The cheapest way to confirm a key works is the index endpoint. It also tells you which scopes the key holds and lists every endpoint it can reach:
curl https://helios-app-mu.vercel.app/api/v1 \
-H "Authorization: Bearer ark_live_your_key_here"
{ "ok": true, "tenant": "…", "your_scopes": ["read"], "endpoints": { … } }Key scopes
Each key carries scopes that control what it can do. You pick them when you create the key:
- read: fetch data (all the GET endpoints). Safe for reporting scripts and dashboards.
- write: also create and update data (the POST and PATCH endpoints). Write implies read.
Best practice: give each integration its own key with the least access it needs. A nightly report that only reads data should get a read-only key. Calling a write endpoint with a read-only key returns 403 insufficient_scope, and the message names the scope that was missing.
Endpoints
Base URL: /api/v1 on the same origin you use Arka at. The examples on this page use https://helios-app-mu.vercel.app. Times are UTC ISO-8601, dates are YYYY-MM-DD, and money values are returned as strings so no precision is lost in transit (send plain numbers when you write).
GET/api/v1/projects
List your projects, newest first. Optional ?limit= (default 50, max 200). Pages by cursor: the response carries next_cursor, and passing it back as ?cursor= returns the next page. null means there is no more.
{
"data": [
{ "id": "…", "name": "Bikaner 25MW", "project_code": "BKN-25",
"project_type": "epc_construction", "status": "active",
"contract_value": "925000000.00", "start_date": "2026-07-06",
"target_completion_date": "2027-03-31", "created_at": "2026-07-01T09:12:00+00:00" }
],
"count": 1,
"next_cursor": null
}GET/api/v1/projects/{id}
One project, including its phases (each with a name, status and sequence order).
GET/api/v1/projects/{id}/tasks
All tasks in a project, ordered by WBS code.
POST/api/v1/projects/{id}/tasks
Create a top-level task, appended after the existing ones. Requires a write key. title is the only required field; priority is one of low, medium (the default), high, critical.
curl -X POST https://helios-app-mu.vercel.app/api/v1/projects/{id}/tasks \
-H "Authorization: Bearer ark_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: task-sync-0042" \
-d '{ "title": "Site survey", "planned_start": "2026-07-10",
"planned_end": "2026-07-20", "priority": "high" }'PATCH/api/v1/projects/{id}/tasks/{taskId}
Update a task. Requires a write key. Accepts any of status, percent_complete, planned_start, planned_end, priority. Two deliberate limits:
- The API cannot declare a task finished. Sending status: "done" or percent_complete: 100 is refused with 409 signoff_required. Finishing a task needs its approver's sign-off, which happens in Arka. Everything below 100 percent is free.
- It can answer 202. If your organisation requires approval for task edits, the change is recorded as a request and is not yet in effect: the response is 202 pending_approval with a change_request_id. Treat 202 as "submitted", never as "saved".
GET/api/v1/projects/{id}/risks
A project's risk register, highest risk score first.
GET/api/v1/projects/{id}/invoices
A project's invoices, newest first, with status, amounts and due and paid dates.
GET/api/v1/projects/{id}/cost-entries
Costs booked against a project. Optional ?limit= (default 100, max 500), ?offset=, and ?since=YYYY-MM-DD to sync incrementally.
POST/api/v1/projects/{id}/cost-entries
The main integration point: push a cost that already exists in your accounting system (a purchase invoice, a payroll allocation, plant hire) and the project's cost figures follow. Requires a write key. Required fields: category (your cost code, it becomes the grouping key), amount, entry_date. Optional: description, currency (ISO-4217, default INR).
curl -X POST https://helios-app-mu.vercel.app/api/v1/projects/{id}/cost-entries \
-H "Authorization: Bearer ark_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: tally-voucher-8813" \
-d '{ "category": "CIVIL", "description": "Module mounting structures",
"amount": 1250000, "entry_date": "2026-08-14" }'A cost in any currency other than INR must carry amount_inr or exchange_rate_to_inr, or it is refused: a foreign-currency cost silently counted at face value is how a USD 100,000 line reads as one lakh rupees.
GET/api/v1/projects/{id}/settlements
Cash that actually moved on a project: receipts and payments. Same limit, offset and since parameters as cost entries.
POST/api/v1/projects/{id}/settlements
Record one movement of cash. Requires a write key. Required: direction (receipt or payment), amount, settled_on. Optional: method, reference (a UTR or cheque number, whatever reconciles back), counterparty, category, notes.
schedule_id is optional on purpose. Supplied, the settlement lands against that planned cash line and the line's balance and status recompute; the line's direction and currency must both match, or the request is refused. Omitted, the money is recorded as off-schedule cash, which still counts in the project's position. Settling more than a line still has outstanding is refused with 409 unless you send allow_overpayment: true, so a mistyped extra zero cannot read as "settled in full".
Cost entries and settlements belong to the finance and cash-schedule modules. If a module is switched off for your organisation, its endpoints answer 403, with a message naming the module and saying an administrator can turn it back on.
Idempotency
Every create endpoint accepts an Idempotency-Key header: any unique string you generate. If Arka has already processed that key for your organisation, it replays the original response (marked with an Idempotency-Replayed: true header) instead of creating a second record. Send one on every create from an automated sync, so a timeout and retry cannot book the same cost twice.
Errors
Errors return a JSON body with an error code and a human message, and a matching HTTP status:
- 401 unauthorized: missing, malformed, unknown or revoked key
- 403 insufficient_scope: the key lacks the scope this endpoint requires
- 403 feature_disabled: API access is switched off for your organisation in Settings
- 403 module switched off: the endpoint exists but the module behind it is off for your organisation. This one body carries error as a plain sentence naming the module, plus feature holding its key, rather than an error code. It is never a 404, because the module is there and an administrator can switch it back on
- 404 not_found: no such record in your organisation. Deliberately identical whether the id does not exist or belongs to someone else, because confirming another organisation's id exists would itself leak
- 400 bad_request: the body was not valid JSON, or there was nothing to update
- 400 validation_error: a field is missing or wrong; details names the offending fields
- 409: a rule refused the request, with the reason spelled out: task completion needs sign-off (signoff_required), or a settlement exceeds what a line has outstanding (conflict)
- 429 rate_limited: too many requests; slow down
Rate limits
Each API key allows 60 requests per minute by default. The ceiling is per key and can be raised for a specific integration: ask. When you hit it, the 429 response carries a Retry-After header saying how many seconds to wait. Requests without a valid key are separately capped per IP address. Your admin can see each key's calls over the last 30 days, its last-used time and its limit under Settings → API Keys.
Webhooks (real-time events)
The endpoints above let you pull data. Webhooks are the reverse: Arka pushes a message to a URL you register the moment something happens, so your system reacts instantly instead of polling. Register a URL under Settings → Webhooks; the signing secret is shown once, when the endpoint is created.
Events you can subscribe to
- task.created: a task was created
- task.completed: a task was completed
- project.status_changed: a project changed status
- invoice.paid: an invoice was marked paid
- risk.triggered: a risk was marked triggered
- workorder.raised: a work order was raised
- settlement.recorded: a receipt or payment was recorded
- settlement.reversed: a recorded settlement was reversed. The one cash event that changes a figure someone may already have acted on
- compliance.expiring: a bank guarantee, insurance policy or licence is nearing expiry
- receivable.overdue: money owed to you did not arrive by its expected date
- payable.overdue: money you owe fell overdue
- share.accepted: an outside party accepted a project you shared
Subscribe to specific events, or to * for all of them. We POST a JSON body:
{
"event": "task.created",
"created_at": "2026-07-06T10:00:00.000Z",
"data": { "project_id": "…", "task": { "id": "…", "title": "…" } }
}Paste a Slack or Microsoft Teams webhook URL and Arka detects it and sends a message formatted for that channel instead of the raw JSON. Chat deliveries carry no signature header; the signature below is for your own endpoints.
Verifying it came from Arka
Every delivery to your own endpoint carries two headers: X-Arka-Event, naming the event, and X-Arka-Signature: sha256=…, an HMAC-SHA256 of the exact request body using your endpoint's secret. Recompute it on your side and compare. If it matches, the event is genuinely from us. Reject anything that does not match.
// Node example
const expected = crypto.createHmac('sha256', endpointSecret)
.update(rawRequestBody).digest('hex');
if (`sha256=${expected}` !== req.headers['x-arka-signature']) reject();Respond with a 2xx status within 8 seconds to acknowledge. A delivery that fails (a non-2xx answer, or no answer in time) is retried automatically, up to four further attempts spread over the 24 hours after the event. Every attempt and its response status are logged under Settings → Webhooks, so you can see failures.
This is v1. It intentionally covers the most common integration needs: read your data, push tasks, costs and cash in, receive events. Need an endpoint that is not here yet? Tell us what you are building and we will add it.