Webhooks
How we call your endpoints, handle failures, and keep things reliable.
How It Works
When a job triggers, Prikke makes an HTTP request to your configured URL. Your endpoint processes the request and returns a response.
- Job triggers (cron schedule or one-time)
- Prikke sends HTTP request to your URL
- Your server processes the request
- Return 2xx status for success
- We log the result and schedule retries if needed
Request Format
Every webhook request includes these headers:
POST /your-endpoint HTTP/1.1
Host: your-app.com
Content-Type: application/json
User-Agent: Prikke/1.0
X-Prikke-Job-Id: job_abc123
X-Prikke-Execution-Id: exec_xyz789
X-Prikke-Signature: sha256=abcdef...
| Header | Description |
|---|---|
X-Prikke-Job-Id | The ID of the job being executed |
X-Prikke-Execution-Id | Unique ID for this execution attempt |
X-Prikke-Signature | HMAC signature for verifying authenticity |
Verifying Signatures
To ensure requests come from Prikke, verify the signature using your webhook secret:
// Node.js example
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expected}` === signature;
}
// In your handler
app.post('/webhook', (req, res) => {
const signature = req.headers['x-prikke-signature'];
const isValid = verifySignature(
JSON.stringify(req.body),
signature,
process.env.PRIKKE_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the job...
});
Response Handling
Prikke interprets your response status code:
| Status | Result | Action |
|---|---|---|
2xx | Success | Job marked complete |
4xx | Client error | Job marked failed (no retry) |
5xx | Server error | Job retried with backoff |
| Timeout | Timeout | Job retried with backoff |
Timeouts
Your endpoint has 30 seconds to respond. If no response is received within this time, the execution is marked as a timeout and scheduled for retry.
Automatic Retries
Retry behavior depends on the job type:
One-time Jobs (run_at)
One-time jobs automatically retry with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 8 hours |
After 5 failed attempts, the job is marked as permanently failed and you'll receive an alert (if configured).
Recurring Jobs (cron)
Cron jobs do not retry. If a scheduled run fails, the next scheduled run will execute as normal. This prevents retry storms and duplicate executions.
Idempotency
Your webhook handler should be idempotent - safe to call multiple times with the same data. This is important because:
- Network issues may cause duplicate deliveries
- Retries will re-send the same request
- A timeout might occur after your server processed the request
Use the X-Prikke-Execution-Id header to deduplicate:
// Track processed executions
const processed = new Set(); // Use Redis in production
app.post('/webhook', async (req, res) => {
const executionId = req.headers['x-prikke-execution-id'];
if (processed.has(executionId)) {
return res.json({ status: 'already_processed' });
}
// Process the job...
await doWork();
processed.add(executionId);
res.json({ status: 'ok' });
});
Custom Headers
You can configure custom headers when creating a job:
curl -X POST https://prikke.whitenoise.no/api/jobs \
-H "Authorization: Bearer pk_live_xxx" \
-d '{"name": "My job", "url": "https://myapp.com/webhook", "cron": "0 * * * *", "headers": {"Authorization": "Bearer my-app-token"}}'