Overview
The V2 ingest endpoint (POST /api/v2/ingest) uses HMAC-SHA256 signatures to authenticate requests. This provides a secure, stateless authentication mechanism optimized for high-throughput event ingestion.
Prerequisites
Before you can sign requests, you need your project's HMAC secret:
- Navigate to your project in the dashboard
- Go to Project Settings
- Copy the HMAC secret, or retrieve it via the API:
bash
curl -X GET https://contox.dev/api/projects/proj_abc123/hmac-secret \
-H "Authorization: Bearer contox_sk_yourkey"
Signing process
Step 1: Prepare the request body
Serialize your request payload as a JSON string. The body must be valid JSON:
json
{
"event": "session_save",
"payload": {
"summary": "Implemented user authentication",
"changes": []
}
}
Step 2: Get the current timestamp
Generate a Unix timestamp (seconds since epoch):
javascript
const timestamp = Math.floor(Date.now() / 1000);
Step 3: Build the signing string
Concatenate the timestamp and the raw request body with a period (.) separator:
{timestamp}.{body}
For example:
1705312200.{"event":"session_save","payload":{"summary":"Implemented user authentication","changes":[]}}
Step 4: Compute the HMAC-SHA256 signature
Use your project's HMAC secret to compute the signature:
javascript
const crypto = require('crypto');
const signingString = `${timestamp}.${body}`;
const signature = crypto
.createHmac('sha256', hmacSecret)
.update(signingString)
.digest('hex');
Step 5: Send the request
Include the required headers with your request:
bash
curl -X POST https://contox.dev/api/v2/ingest \
-H "Content-Type: application/json" \
-H "X-Contox-Project: proj_abc123" \
-H "X-Contox-Timestamp: 1705312200" \
-H "X-Contox-Signature: sha256=a1b2c3d4e5f6..." \
-d '{"event":"session_save","payload":{...}}'
Required headers
| Header | Description |
|---|---|
X-Contox-Project | Your project ID |
X-Contox-Timestamp | Unix timestamp used in signing |
X-Contox-Signature | The computed signature, prefixed with sha256= |
Timestamp validation
The server rejects requests where the timestamp is more than 5 minutes old. This prevents replay attacks. Ensure your system clock is synchronized.
Complete examples
Node.js
javascript
const crypto = require('crypto');
async function ingestEvent(projectId, hmacSecret, event) {
const body = JSON.stringify(event);
const timestamp = Math.floor(Date.now() / 1000);
const signingString = `${timestamp}.${body}`;
const signature = crypto
.createHmac('sha256', hmacSecret)
.update(signingString)
.digest('hex');
const response = await fetch('https://contox.dev/api/v2/ingest', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Contox-Project': projectId,
'X-Contox-Timestamp': String(timestamp),
'X-Contox-Signature': `sha256=${signature}`,
},
body,
});
return response.json();
}
Python
python
import hmac
import hashlib
import json
import time
import requests
def ingest_event(project_id, hmac_secret, event):
body = json.dumps(event, separators=(',', ':'))
timestamp = str(int(time.time()))
signing_string = f"{timestamp}.{body}"
signature = hmac.new(
hmac_secret.encode(),
signing_string.encode(),
hashlib.sha256
).hexdigest()
response = requests.post(
'https://contox.dev/api/v2/ingest',
headers={
'Content-Type': 'application/json',
'X-Contox-Project': project_id,
'X-Contox-Timestamp': timestamp,
'X-Contox-Signature': f'sha256={signature}',
},
data=body,
)
return response.json()
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
Invalid signature | Signature mismatch | Verify the signing string format and secret |
Timestamp expired | Request too old | Synchronize your system clock |
Missing required header | Header not included | Include all three X-Contox-* headers |
Next steps
- V2 Ingest Endpoint -- Full endpoint documentation
- Rate Limits -- Understand request limits