Connect your ERP, HR platform, or ticketing system to route approvals through your existing matrix — without changing your core systems.
REST
JSON API
60/min
Rate limit
v1
Stable API
Four steps from zero to live approval routing in your system.
Create a key from your settings panel. Scope it to requests:write and requests:read.
Each member has an externalRef — your system's employee ID. Use it as submitterRef in requests.
POST to /v1/requests with your metadata. The platform finds the matching approval matrix automatically.
Check status with GET /v1/requests/:id. Webhooks coming soon for real-time push notifications.
All endpoints are under https://api.yourapp.com and return JSON.
/v1/requests Submit a new approval request {
"submitterRef": "EMP-1042",
"requestType": "purchase_order",
"title": "Office Supplies Q3",
"amount": 4500,
"currency": "USD",
"metadata": {
"department": "Engineering",
"poNumber": "PO-9871"
}
} {
"requestId": "req_abc123",
"status": "pending",
"matrixId": "mat_xyz789",
"createdAt": "2025-01-15T10:30:00Z"
} | Field | Type | Required |
|---|---|---|
| submitterRef | string | Yes |
| requestType | string | Yes |
| title | string | Yes |
| amount | number | No |
| currency | string | No |
| metadata | object | No |
# Submit a new approval request
curl -X POST https://api.yourapp.com/v1/requests \
-H "Authorization: Bearer apx_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"submitterRef":"EMP-1042","requestType":"purchase_order","title":"Office Supplies Q3","amount":4500}' // Submit a new approval request
const res = await fetch('https://api.yourapp.com/v1/requests', {
method: 'POST',
headers: {
'Authorization': 'Bearer apx_live_YOUR_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
submitterRef: 'EMP-1042',
requestType: 'purchase_order',
title: 'Office Supplies Q3',
amount: 4500,
}),
});
const { requestId, status } = await res.json(); import requests
resp = requests.post(
'https://api.yourapp.com/v1/requests',
headers={'Authorization': 'Bearer apx_live_YOUR_KEY'},
json={
'submitterRef': 'EMP-1042',
'requestType': 'purchase_order',
'title': 'Office Supplies Q3',
'amount': 4500,
}
)
data = resp.json() /v1/requests/:requestId Get current status of a request | Param | Description |
|---|---|
| requestId | The ID returned when the request was created (e.g. req_abc123) |
{
"requestId": "req_abc123",
"status": "approved",
"title": "Office Supplies Q3",
"submitterRef": "EMP-1042",
"currentStage": null,
"totalStages": 2,
"decisions": [
{
"stage": 1,
"decidedBy": "Jane Smith",
"decision": "approved",
"decidedAt": "2025-01-15T11:00:00Z"
},
{
"stage": 2,
"decidedBy": "Bob Lee",
"decision": "approved",
"decidedAt": "2025-01-15T14:22:00Z"
}
]
} curl https://api.yourapp.com/v1/requests/req_abc123 \
-H "Authorization: Bearer apx_live_YOUR_KEY" const res = await fetch('https://api.yourapp.com/v1/requests/req_abc123', {
headers: { 'Authorization': 'Bearer apx_live_YOUR_KEY' },
});
const request = await res.json(); import requests
resp = requests.get(
'https://api.yourapp.com/v1/requests/req_abc123',
headers={'Authorization': 'Bearer apx_live_YOUR_KEY'},
)
data = resp.json() /v1/requests/test
Dry-run
Validate payload without creating a request Integration testing endpoint
Accepts the same payload as POST /v1/requests. Validates your data, resolves the matching approval matrix, and returns what a real request would look like — without creating anything or notifying any approvers.
Same fields as POST /v1/requests.
{
"requestId": null,
"status": "pending",
"matrixId": "mat_xyz789",
"matrixName": "Engineering PO Matrix",
"dryRun": true,
"createdAt": null
} # Dry-run: validate without creating
curl -X POST https://api.yourapp.com/v1/requests/test \
-H "Authorization: Bearer apx_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"submitterRef":"EMP-1042","requestType":"purchase_order","title":"Office Supplies Q3","amount":4500}' // Dry-run: validate without creating
const res = await fetch('https://api.yourapp.com/v1/requests/test', {
method: 'POST',
headers: {
'Authorization': 'Bearer apx_live_YOUR_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
submitterRef: 'EMP-1042',
requestType: 'purchase_order',
title: 'Office Supplies Q3',
amount: 4500,
}),
});
const { matrixName, dryRun } = await res.json(); # Dry-run: validate without creating
import requests
resp = requests.post(
'https://api.yourapp.com/v1/requests/test',
headers={'Authorization': 'Bearer apx_live_YOUR_KEY'},
json={
'submitterRef': 'EMP-1042',
'requestType': 'purchase_order',
'title': 'Office Supplies Q3',
'amount': 4500,
}
)
data = resp.json() # data['dryRun'] == True Everything you need to know about keeping your integration secure.
All keys start with apx_live_ followed by a 32-character cryptographically random string.
Send your key in every request as Authorization: Bearer apx_live_.... Query-string auth is not supported.
Keys are strictly scoped to your tenant. A key cannot read or write data belonging to any other organization — ever.
60 requests per minute per API key. Exceeding the limit returns HTTP 429 Too Many Requests with a Retry-After header.
Revoke any key immediately from your settings panel. Revoked keys become invalid within milliseconds — no propagation delay.
Do not hardcode keys in source code or commit them to version control. Always load keys from environment variables or a secrets manager.
# .env (never commit this file)
APPROVALMATRIX_API_KEY=apx_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Node.js
const apiKey = process.env.APPROVALMATRIX_API_KEY;
# Python
import os
api_key = os.environ["APPROVALMATRIX_API_KEY"] Create your account and generate your first API key in minutes.