API Documentation
Full reference: routes, authentication, CORS, examples and what to try on each endpoint.
Introduction
What problem Jobcelis solves, use cases, and API capabilities.
Every time you build an app that needs to notify other services, you end up writing the same logic: HTTP clients, retries, failure handling, delivery logs. In every project, reimplementing the same thing.
Jobcelis centralizes that infrastructure into one API. Send any JSON via HTTP POST — the platform routes, filters, transforms, and delivers to the configured destination URLs. No fixed schemas: topics, payloads, and rules are freely defined.
Usage examples
E-commerce
Order created → notify warehouse + billing + confirmation email
Payments
Payment completed → update order + send receipt + log in accounting
CI/CD
Successful deploy → monitoring + Slack + analytics tracker
SaaS
User registered → CRM + welcome email + analytics
Main capabilities
- Events: JSON submission with optional topic via POST. No schema restrictions.
- Webhooks: Destination URLs, filters (topic, amount, status), body_config (full, pick, rename, extra).
- Deliveries: automatic retries with exponential backoff.
- Jobs: daily, weekly, monthly or cron expression scheduling. Emits events or executes POST to destination URLs.
- Topics: labels to filter and organize events.
Route groups
- Public routes: register, login, refresh JWT. No API Key.
- API Key routes: events, webhooks, deliveries, jobs, project. Token in Dashboard.
Responses in JSON. CORS enabled. Max payload 256 KB.
Core Concepts
Core platform concepts.
What is an event?
An event is a JSON message sent to Jobcelis via HTTP POST. Accepts any JSON structure: orders, payments, records, etc. Optionally includes a topic for classification. Jobcelis persists the event and, based on configured webhooks, delivers the information to specified destination URLs.
Example
{"topic": "order.created", "order_id": 123, "total": 99.99}
What is a webhook?
A webhook is a destination URL configured in the project. When an event matches the specified conditions (topic, filters), Jobcelis performs a POST to that URL with the configured payload.
What is a delivery?
Each event delivery attempt to a webhook URL generates a delivery record. The record has states: pending, success, or failed. On failure, Jobcelis automatically retries with exponential backoff.
What is a job?
A job is a scheduled task with daily, weekly, monthly, or cron expression scheduling. On execution, it emits an internal event or performs a POST to an external URL.
What is a topic?
A topic is an optional label for classifying events (e.g., order.created, payment.completed). Used as a filtering criterion in webhooks. Naming convention is freely defined by the user.
Quick Start
Initial setup steps for API integration.
Sign up and log in
Create an account on the platform with email and password. Access the Dashboard.
Obtain the API Token
The API token is located in the API Token section of the Dashboard. Store the complete token securely; it is required in all requests with header Authorization: Bearer YOUR_TOKEN or X-Api-Key: YOUR_TOKEN.
Send the first event
Send a POST request to /api/v1/events with a JSON body.
curl -X POST "https://jobcelis.com/api/v1/events" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"test","message":"Hello"}'
Configure a webhook
In the Dashboard, navigate to Webhooks > Create. Configure an accessible URL that accepts POST requests. Optionally, specify a topic to filter events.
Configure a job (optional)
In the Dashboard, navigate to Jobs > Create. Select schedule (daily, weekly, monthly, or cron) and action (emit event or POST to URL).
Base URL
All examples use the current base URL:
https://jobcelis.com
In production, the base URL corresponds to the configured domain. The curl examples include this base.
Authentication
Authentication methods: API Key and JWT.
API Key routes:
Token authentication supports three methods:
-
Header
Authorization: Bearer <token> -
Header
X-Api-Key: <token> -
Query param
?api_key=<token>
The API token is generated in the API Token section of the Dashboard. The complete value is displayed only at creation time. It must be stored securely.
Public routes (Auth)
Authentication endpoints do not require an API Key. Registration and login endpoints return a JWT for application authentication.
CORS
Cross-Origin Resource Sharing (CORS) configuration.
The API has CORS enabled for any origin (Access-Control-Allow-Origin: *). Any frontend (your SPA, another site, mobile WebView, etc.) can consume the API from its own URL without browser blocks.
Allowed headers:
-
Authorization -
X-Api-Key -
Content-Type,Accept
Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS.
Auth (register/login)
Public routes without API Key. Body and response in JSON.
/api/v1/auth/register
Create a new account.
curl -X POST "https://jobcelis.com/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{"name":"Test","email":"test@example.com","password":"SecurePass123!"}'
{
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "test@example.com",
"name": "Test"
},
"token": "eyJhbGciOiJIUzI1NiIs...",
"api_key": "jc_live_a1b2c3d4e5f6..."
}
/api/v1/auth/login
Log in and get JWT.
curl -X POST "https://jobcelis.com/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"SecurePass123!"}'
If MFA is enabled, mfa_required is returned instead of the token.
{
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "test@example.com",
"name": "Test"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}
{
"mfa_required": true,
"mfa_token": "eyJhbGciOiJIUzI1NiIs..."
}
/api/v1/auth/refresh
Renew a JWT.
curl -X POST "https://jobcelis.com/api/v1/auth/refresh" \
-H "Content-Type: application/json" \
-d '{"token":"YOUR_JWT"}'
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
/api/v1/auth/mfa/verify
Verify MFA code after login.
curl -X POST "https://jobcelis.com/api/v1/auth/mfa/verify" \
-H "Content-Type: application/json" \
-d '{"mfa_token":"TEMP_TOKEN","code":"123456"}'
{
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "test@example.com",
"name": "Test"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Events
Event management: creation, listing, and retrieval.
/api/v1/events
Send a new event.
curl -X POST "https://jobcelis.com/api/v1/events" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"order.created","order_id":"12345","amount":99.99}'
{
"event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"payload_hash": "sha256:a3f2b8c1d4e5..."
}
/api/v1/send
Alias for POST /api/v1/events. Shortcut to send an event with the same body format and response.
curl -X POST "https://jobcelis.com/api/v1/send" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"order.created","order_id":"12345","amount":99.99}'
{
"event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"payload_hash": "sha256:a3f2b8c1d4e5..."
}
/api/v1/events
List events with pagination.
curl "https://jobcelis.com/api/v1/events?limit=10" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"events": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"topic": "order.created",
"payload": {"order_id": "12345", "amount": 99.99},
"status": "active",
"occurred_at": "2026-01-15T10:30:00Z",
"deliver_at": null,
"payload_hash": "sha256:a3f2b8c1d4e5...",
"idempotency_key": null,
"inserted_at": "2026-01-15T10:30:00Z"
}
],
"has_next": true,
"next_cursor": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
/api/v1/events/:id
Event detail
curl "https://jobcelis.com/api/v1/events/EVENT_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"topic": "order.created",
"payload": {"order_id": "12345", "amount": 99.99},
"status": "active",
"occurred_at": "2026-01-15T10:30:00Z",
"deliver_at": null,
"payload_hash": "sha256:a3f2b8c1d4e5...",
"idempotency_key": null,
"inserted_at": "2026-01-15T10:30:00Z",
"deliveries": [
{"id": "d4e5f6a7-b8c9-0123-defg-234567890123", "status": "success", "attempt_number": 1}
]
}
/api/v1/events/:id
Delete an event.
curl -X DELETE "https://jobcelis.com/api/v1/events/EVENT_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"status": "inactive"
}
Webhooks
Create, list and manage webhooks.
/api/v1/webhooks
List project webhooks.
curl "https://jobcelis.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"webhooks": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"url": "https://example.com/hook",
"status": "active",
"topics": ["order.*"],
"filters": [],
"body_config": {},
"headers": {},
"retry_config": {},
"inserted_at": "2026-01-10T08:00:00Z"
}
]
}
/api/v1/webhooks
Create a webhook
curl -X POST "https://jobcelis.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hook","topics":["order.*"]}'
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"url": "https://example.com/hook",
"status": "active",
"topics": ["order.*"],
"filters": [],
"body_config": {},
"headers": {},
"retry_config": {},
"inserted_at": "2026-01-10T08:00:00Z"
}
/api/v1/webhooks/:id
Webhook details.
curl "https://jobcelis.com/api/v1/webhooks/WEBHOOK_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"url": "https://example.com/hook",
"status": "active",
"topics": ["order.*"],
"filters": [],
"body_config": {},
"headers": {},
"retry_config": {},
"inserted_at": "2026-01-10T08:00:00Z"
}
/api/v1/webhooks/:id
Update a webhook.
curl -X PATCH "https://jobcelis.com/api/v1/webhooks/WEBHOOK_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"active":false}'
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"url": "https://example.com/hook",
"status": "active",
"topics": ["order.*"],
"filters": [],
"body_config": {},
"headers": {},
"retry_config": {},
"inserted_at": "2026-01-10T08:00:00Z"
}
/api/v1/webhooks/:id
Delete a webhook.
curl -X DELETE "https://jobcelis.com/api/v1/webhooks/WEBHOOK_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"status": "inactive"
}
/api/v1/webhooks/:id/health
Retrieves the health status of a webhook.
curl "https://jobcelis.com/api/v1/webhooks/WEBHOOK_ID/health" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"webhook_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"url": "https://example.com/hook",
"health": {
"status": "healthy",
"success_rate": 0.98,
"avg_latency_ms": 120,
"last_delivery_at": "2026-01-15T10:30:00Z"
}
}
Deliveries
Delivery history and retries.
/api/v1/deliveries
List deliveries with optional filters.
curl "https://jobcelis.com/api/v1/deliveries?status=failed&limit=10" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"deliveries": [
{
"id": "d4e5f6a7-b8c9-0123-defg-234567890123",
"event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"webhook_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"status": "failed",
"attempt_number": 3,
"response_status": 500,
"next_retry_at": "2026-01-15T11:00:00Z",
"inserted_at": "2026-01-15T10:30:00Z"
}
],
"has_next": false,
"next_cursor": null
}
/api/v1/deliveries/:id/retry
Retry a failed delivery.
curl -X POST "https://jobcelis.com/api/v1/deliveries/DELIVERY_ID/retry" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"status": "retry_queued"
}
Jobs
Scheduled tasks: daily, weekly, monthly or cron.
/api/v1/jobs
List project jobs.
curl "https://jobcelis.com/api/v1/jobs" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"jobs": [
{
"id": "e5f6a7b8-c9d0-1234-efgh-345678901234",
"name": "Daily Report",
"schedule_type": "daily",
"schedule_config": {},
"action_type": "emit_event",
"action_config": {"topic": "report.daily", "payload": {}},
"status": "active",
"inserted_at": "2026-01-05T12:00:00Z"
}
]
}
/api/v1/jobs
Create a scheduled job.
curl -X POST "https://jobcelis.com/api/v1/jobs" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Daily Report","schedule_type":"daily","schedule_hour":0,"action_type":"emit_event","action_config":{"topic":"report.daily","payload":{}}}'
{
"id": "e5f6a7b8-c9d0-1234-efgh-345678901234",
"name": "Daily Report",
"schedule_type": "daily",
"schedule_config": {},
"action_type": "emit_event",
"action_config": {"topic": "report.daily", "payload": {}},
"status": "active",
"inserted_at": "2026-01-15T10:30:00Z"
}
/api/v1/jobs/:id
Event detail
curl "https://jobcelis.com/api/v1/jobs/JOB_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"id": "e5f6a7b8-c9d0-1234-efgh-345678901234",
"name": "Daily Report",
"schedule_type": "daily",
"schedule_config": {},
"action_type": "emit_event",
"action_config": {"topic": "report.daily", "payload": {}},
"status": "active",
"inserted_at": "2026-01-05T12:00:00Z",
"recent_runs": [
{"id": "f6a7b8c9-d0e1-2345-fghi-456789012345", "executed_at": "2026-01-15T00:00:00Z", "status": "success", "result": null}
]
}
/api/v1/jobs/:id
Updating...
curl -X PATCH "https://jobcelis.com/api/v1/jobs/JOB_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Updated Report","schedule_hour":6}'
{
"id": "e5f6a7b8-c9d0-1234-efgh-345678901234",
"name": "Updated Report",
"schedule_type": "daily",
"schedule_config": {},
"action_type": "emit_event",
"action_config": {"topic": "report.daily", "payload": {}},
"status": "active",
"inserted_at": "2026-01-05T12:00:00Z"
}
/api/v1/jobs/:id
Deleting...
curl -X DELETE "https://jobcelis.com/api/v1/jobs/JOB_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"status": "inactive"
}
/api/v1/jobs/:id/runs
Job execution history.
curl "https://jobcelis.com/api/v1/jobs/JOB_ID/runs" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"runs": [
{
"id": "f6a7b8c9-d0e1-2345-fghi-456789012345",
"executed_at": "2026-01-15T00:00:00Z",
"status": "success",
"result": null
}
]
}
/api/v1/jobs/cron-preview
Preview the next executions of a cron expression.
curl "https://jobcelis.com/api/v1/jobs/cron-preview?expression=*/15+*+*+*+*" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"expression": "*/15 * * * *",
"next_executions": [
"2026-01-15T10:45:00Z",
"2026-01-15T11:00:00Z",
"2026-01-15T11:15:00Z",
"2026-01-15T11:30:00Z",
"2026-01-15T11:45:00Z"
]
}
Project & API Token
View and update your project, manage API tokens.
/api/v1/project
Current project details.
curl "https://jobcelis.com/api/v1/project" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "My Project",
"status": "active",
"settings": {}
}
/api/v1/project
Update the project name.
curl -X PATCH "https://jobcelis.com/api/v1/project" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"My Project"}'
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "My Project",
"status": "active"
}
/api/v1/topics
List all used topics.
curl "https://jobcelis.com/api/v1/topics" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"topics": ["order.created", "order.updated", "payment.completed", "user.registered"]
}
/api/v1/token
Show current token info (prefix).
curl "https://jobcelis.com/api/v1/token" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"prefix": "jc_live_a1b2",
"message": "Use Authorization: Bearer <your_key>. Regenerate from dashboard to get a new key."
}
/api/v1/token/regenerate
Regenerate the API token. The previous one stops working.
curl -X POST "https://jobcelis.com/api/v1/token/regenerate" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"token": "jc_live_new_token_value_here...",
"message": "The previous token no longer works. Only this token is valid. Save it; it is only shown once."
}
Pipelines
Event processing pipelines with sequential transformation steps.
/api/v1/pipelines
Lists project pipelines.
curl "https://jobcelis.com/api/v1/pipelines" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "f6a7b8c9-d0e1-2345-fghi-456789012345",
"name": "Order Pipeline",
"status": "active",
"description": "Process orders",
"topics": ["order.*"],
"steps": [{"type": "filter", "config": {"field": "amount", "operator": "gt", "value": 100}}],
"webhook_id": null,
"inserted_at": "2026-01-10T08:00:00Z"
}
]
}
/api/v1/pipelines
Creates a new processing pipeline.
curl -X POST "https://jobcelis.com/api/v1/pipelines" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Order Pipeline","description":"Process orders","steps":[{"type":"filter","config":{"field":"amount","operator":"gt","value":100}}]}'
{
"data": {
"id": "f6a7b8c9-d0e1-2345-fghi-456789012345",
"name": "Order Pipeline",
"status": "active",
"description": "Process orders",
"topics": null,
"steps": [{"type": "filter", "config": {"field": "amount", "operator": "gt", "value": 100}}],
"webhook_id": null,
"inserted_at": "2026-01-15T10:30:00Z"
}
}
/api/v1/pipelines/:id
Retrieves detailed pipeline configuration.
curl "https://jobcelis.com/api/v1/pipelines/PIPELINE_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": {
"id": "f6a7b8c9-d0e1-2345-fghi-456789012345",
"name": "Order Pipeline",
"status": "active",
"description": "Process orders",
"topics": ["order.*"],
"steps": [{"type": "filter", "config": {"field": "amount", "operator": "gt", "value": 100}}],
"webhook_id": null,
"inserted_at": "2026-01-10T08:00:00Z"
}
}
/api/v1/pipelines/:id
Updates pipeline configuration.
curl -X PATCH "https://jobcelis.com/api/v1/pipelines/PIPELINE_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Updated Pipeline","description":"New description"}'
{
"data": {
"id": "f6a7b8c9-d0e1-2345-fghi-456789012345",
"name": "Updated Pipeline",
"status": "active",
"description": "New description",
"topics": ["order.*"],
"steps": [{"type": "filter", "config": {"field": "amount", "operator": "gt", "value": 100}}],
"webhook_id": null,
"inserted_at": "2026-01-10T08:00:00Z"
}
}
/api/v1/pipelines/:id
Deletes a pipeline and its associated configurations.
curl -X DELETE "https://jobcelis.com/api/v1/pipelines/PIPELINE_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
No content.
/api/v1/pipelines/:id/test
Executes a pipeline with test payload without persisting results.
curl -X POST "https://jobcelis.com/api/v1/pipelines/PIPELINE_ID/test" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"order.created","payload":{"order_id":"123","amount":99.99}}'
{
"input": {"order_id": "123", "amount": 99.99},
"output": {"order_id": "123", "amount": 99.99},
"steps_count": 1,
"status": "passed"
}
Dead Letters
Events that exhausted all configured delivery attempts.
/api/v1/dead-letters
Lists undelivered events (dead letters) for the project.
curl "https://jobcelis.com/api/v1/dead-letters" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"dead_letters": [
{
"id": "a7b8c9d0-e1f2-3456-ghij-567890123456",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"delivery_id": "d4e5f6a7-b8c9-0123-defg-234567890123",
"event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"webhook_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"webhook_url": "https://example.com/hook",
"original_payload": {"order_id": "12345"},
"last_error": "Connection refused",
"last_response_status": null,
"attempts_exhausted": 5,
"resolved": false,
"resolved_at": null,
"inserted_at": "2026-01-15T10:30:00Z"
}
]
}
/api/v1/dead-letters/:id
Retrieves full details of a dead letter.
curl "https://jobcelis.com/api/v1/dead-letters/DEAD_LETTER_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"id": "a7b8c9d0-e1f2-3456-ghij-567890123456",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"delivery_id": "d4e5f6a7-b8c9-0123-defg-234567890123",
"event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"webhook_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"webhook_url": "https://example.com/hook",
"original_payload": {"order_id": "12345"},
"last_error": "Connection refused",
"last_response_status": null,
"attempts_exhausted": 5,
"resolved": false,
"resolved_at": null,
"inserted_at": "2026-01-15T10:30:00Z"
}
/api/v1/dead-letters/:id/retry
Retries delivery of a dead letter to the webhook endpoint.
curl -X POST "https://jobcelis.com/api/v1/dead-letters/DEAD_LETTER_ID/retry" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"status": "retrying",
"delivery_id": "d4e5f6a7-b8c9-0123-defg-234567890123"
}
/api/v1/dead-letters/:id/resolve
Marks a dead letter as resolved, removing it from the queue.
curl -X PATCH "https://jobcelis.com/api/v1/dead-letters/DEAD_LETTER_ID/resolve" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"status": "resolved"
}
Event Replay
Re-delivers historical events to webhook endpoints.
/api/v1/replays
Creates a replay job to re-deliver events within a time range.
curl -X POST "https://jobcelis.com/api/v1/replays" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"webhook_id":"WEBHOOK_ID","from":"2026-01-01T00:00:00Z","to":"2026-01-31T23:59:59Z"}'
{
"id": "b8c9d0e1-f2a3-4567-hijk-678901234567",
"status": "pending",
"filters": {"webhook_id": "WEBHOOK_ID", "from_date": "2026-01-01T00:00:00Z", "to_date": "2026-01-31T23:59:59Z"},
"total_events": 0,
"processed_events": 0,
"started_at": null,
"completed_at": null,
"inserted_at": "2026-01-15T10:30:00Z"
}
/api/v1/replays
Lists replay jobs with their status and progress.
curl "https://jobcelis.com/api/v1/replays" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "b8c9d0e1-f2a3-4567-hijk-678901234567",
"status": "completed",
"filters": {"webhook_id": "WEBHOOK_ID", "from_date": "2026-01-01T00:00:00Z"},
"total_events": 42,
"processed_events": 42,
"started_at": "2026-01-15T10:31:00Z",
"completed_at": "2026-01-15T10:32:00Z",
"inserted_at": "2026-01-15T10:30:00Z"
}
]
}
/api/v1/replays/:id
Retrieves the status and configuration of a specific replay.
curl "https://jobcelis.com/api/v1/replays/REPLAY_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"id": "b8c9d0e1-f2a3-4567-hijk-678901234567",
"status": "running",
"filters": {"webhook_id": "WEBHOOK_ID"},
"total_events": 42,
"processed_events": 15,
"started_at": "2026-01-15T10:31:00Z",
"completed_at": null,
"inserted_at": "2026-01-15T10:30:00Z"
}
/api/v1/replays/:id
Cancels an in-progress replay, stopping pending deliveries.
curl -X DELETE "https://jobcelis.com/api/v1/replays/REPLAY_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"id": "b8c9d0e1-f2a3-4567-hijk-678901234567",
"status": "cancelled",
"filters": {"webhook_id": "WEBHOOK_ID"},
"total_events": 42,
"processed_events": 15,
"started_at": "2026-01-15T10:31:00Z",
"completed_at": "2026-01-15T10:35:00Z",
"inserted_at": "2026-01-15T10:30:00Z"
}
Event Schemas
Event structure definition and validation using JSON Schema.
/api/v1/event-schemas
Lists JSON schemas registered for event validation.
curl "https://jobcelis.com/api/v1/event-schemas" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "c9d0e1f2-a3b4-5678-ijkl-789012345678",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "order.created",
"schema": {"type": "object", "required": ["order_id"], "properties": {"order_id": {"type": "string"}}},
"version": 1,
"status": "active",
"inserted_at": "2026-01-10T08:00:00Z",
"updated_at": "2026-01-10T08:00:00Z"
}
]
}
/api/v1/event-schemas
Creates a new JSON Schema for payload validation.
curl -X POST "https://jobcelis.com/api/v1/event-schemas" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"order.created","schema":{"type":"object","required":["order_id"],"properties":{"order_id":{"type":"string"}}}}'
{
"data": {
"id": "c9d0e1f2-a3b4-5678-ijkl-789012345678",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "order.created",
"schema": {"type": "object", "required": ["order_id"], "properties": {"order_id": {"type": "string"}}},
"version": 1,
"status": "active",
"inserted_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
/api/v1/event-schemas/:id
Retrieves the complete JSON Schema definition.
curl "https://jobcelis.com/api/v1/event-schemas/SCHEMA_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": {
"id": "c9d0e1f2-a3b4-5678-ijkl-789012345678",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "order.created",
"schema": {"type": "object", "required": ["order_id"], "properties": {"order_id": {"type": "string"}}},
"version": 1,
"status": "active",
"inserted_at": "2026-01-10T08:00:00Z",
"updated_at": "2026-01-10T08:00:00Z"
}
}
/api/v1/event-schemas/:id
Updates a JSON Schema and increments the version number.
curl -X PATCH "https://jobcelis.com/api/v1/event-schemas/SCHEMA_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"schema":{"type":"object","required":["order_id","amount"],"properties":{"order_id":{"type":"string"},"amount":{"type":"number"}}}}'
{
"data": {
"id": "c9d0e1f2-a3b4-5678-ijkl-789012345678",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "order.created",
"schema": {"type": "object", "required": ["order_id", "amount"], "properties": {"order_id": {"type": "string"}, "amount": {"type": "number"}}},
"version": 1,
"status": "active",
"inserted_at": "2026-01-10T08:00:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
/api/v1/event-schemas/:id
Deletes a JSON Schema from the project.
curl -X DELETE "https://jobcelis.com/api/v1/event-schemas/SCHEMA_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"ok": true
}
/api/v1/event-schemas/validate
Validates a payload against a JSON Schema without persisting the event.
curl -X POST "https://jobcelis.com/api/v1/event-schemas/validate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"order.created","payload":{"order_id":"123"}}'
{
"valid": true,
"errors": []
}
// If validation fails:
{
"valid": false,
"errors": [
{"message": "Required property order_id is missing", "path": "#/order_id"}
]
}
Data Export
Project data export in CSV or JSON format.
/api/v1/export/events
Exports events with full metadata and payload.
curl "https://jobcelis.com/api/v1/export/events?format=csv" \
-H "Authorization: Bearer YOUR_TOKEN"
CSV format by default. Use ?format=json for JSON output.
id,topic,status,occurred_at,payload,payload_hash
b2c3d4e5-...,order.created,active,2026-01-15T10:30:00Z,"{""order_id"":""12345""}",sha256:a3f2...
/api/v1/export/deliveries
Exports delivery records with attempt history.
curl "https://jobcelis.com/api/v1/export/deliveries?format=csv" \
-H "Authorization: Bearer YOUR_TOKEN"
CSV format by default. Use ?format=json for JSON output.
id,event_id,webhook_id,status,attempt_number,response_status,inserted_at
d4e5f6a7-...,b2c3d4e5-...,c3d4e5f6-...,success,1,200,2026-01-15T10:30:00Z
/api/v1/export/jobs
Exports scheduled jobs with status and configuration.
curl "https://jobcelis.com/api/v1/export/jobs?format=csv" \
-H "Authorization: Bearer YOUR_TOKEN"
CSV format by default. Use ?format=json for JSON output.
id,name,status,schedule_type,action_type,inserted_at
e5f6a7b8-...,Daily Report,active,daily,emit_event,2026-01-05T12:00:00Z
/api/v1/export/audit-log
Exports the complete audit log with action metadata.
curl "https://jobcelis.com/api/v1/export/audit-log?format=csv" \
-H "Authorization: Bearer YOUR_TOKEN"
CSV format by default. Use ?format=json for JSON output.
id,action,resource_type,resource_id,user_id,ip_address,inserted_at
f6a7b8c9-...,event.created,event,b2c3d4e5-...,a1b2c3d4-...,192.168.1.1,2026-01-15T10:30:00Z
Dashboard
Main control panel for project resource management.
The Dashboard is the primary management interface. Enables viewing events, webhooks, deliveries, jobs, analytics, audit log, and API token management. Accessed via the web application after authentication.
Main sections
- Events: list, detail, filters
- Webhooks: create, edit, enable/disable
- Deliveries: history, retries
- Jobs: create, edit, view executions
Tools
- Analytics: charts and metrics
- Audit Log: action records
- Sandbox: test endpoints
- API Token: view and regenerate
Account Settings
Profile, credentials, and multi-factor authentication management.
From the account page, name, email, and password can be updated. Two-factor authentication (MFA) can also be enabled for additional security.
Password Recovery
Credential recovery process.
If the password is lost, request a recovery link from the login page. An email with a temporary link will be sent to set a new password.
Multi-project
Multi-project management from a single account.
Multiple isolated projects can be created, each with its own API token, webhooks, events, and configuration. Enables separation of environments (dev, staging, prod) or different applications.
/api/v1/projects
Lists projects associated with the authenticated account (requires JWT).
curl "https://jobcelis.com/api/v1/projects" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Production",
"status": "active",
"is_default": true,
"settings": {},
"inserted_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-10T08:00:00Z"
}
]
}
/api/v1/projects
Creates a new project with default settings and generates an API token.
curl -X POST "https://jobcelis.com/api/v1/projects" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"My New Project"}'
{
"data": {
"id": "d0e1f2a3-b4c5-6789-klmn-890123456789",
"name": "My New Project",
"status": "active",
"is_default": false,
"settings": {},
"inserted_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
/api/v1/projects/:id/default
Sets a project as the default for API requests.
curl -X PATCH "https://jobcelis.com/api/v1/projects/PROJECT_ID/default" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": {
"id": "d0e1f2a3-b4c5-6789-klmn-890123456789",
"name": "My New Project",
"status": "active",
"is_default": true,
"settings": {},
"inserted_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:35:00Z"
}
}
/api/v1/projects/:id
Retrieves the details of a specific project by its ID (requires JWT).
curl "https://jobcelis.com/api/v1/projects/PROJECT_ID" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Production",
"status": "active",
"is_default": true,
"settings": {},
"inserted_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-10T08:00:00Z"
}
}
/api/v1/projects/:id
Updates the name, settings, or retention policy of an existing project.
curl -X PATCH "https://jobcelis.com/api/v1/projects/PROJECT_ID" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Updated Project Name","settings":{"notifications":true}}'
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Updated Project Name",
"status": "active",
"is_default": true,
"settings": {"notifications": true},
"inserted_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-15T12:00:00Z"
}
}
/api/v1/projects/:id
Deletes a project (only the owner can do this). Associated data is deactivated.
curl -X DELETE "https://jobcelis.com/api/v1/projects/PROJECT_ID" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"ok": true
}
Teams
Team management and permissions in shared projects.
Allows inviting members to projects with differentiated roles (admin, member, viewer). Invited members receive an email notification and must accept the invitation to gain access.
/api/v1/projects/:id/members
Lists project members with their assigned roles.
curl "https://jobcelis.com/api/v1/projects/PROJECT_ID/members" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "e1f2a3b4-c5d6-7890-lmno-901234567890",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "f2a3b4c5-d6e7-8901-mnop-012345678901",
"role": "member",
"status": "accepted",
"invited_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2026-01-10T08:00:00Z"
}
]
}
/api/v1/projects/:id/members
Sends an invitation to add a member to the project.
curl -X POST "https://jobcelis.com/api/v1/projects/PROJECT_ID/members" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"member@example.com","role":"member"}'
{
"data": {
"id": "e1f2a3b4-c5d6-7890-lmno-901234567890",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "f2a3b4c5-d6e7-8901-mnop-012345678901",
"role": "member",
"status": "pending",
"invited_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2026-01-15T10:30:00Z"
}
}
/api/v1/projects/:id/members/:mid
Modifies the role assigned to a project member.
curl -X PATCH "https://jobcelis.com/api/v1/projects/PROJECT_ID/members/MEMBER_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role":"admin"}'
{
"data": {
"id": "e1f2a3b4-c5d6-7890-lmno-901234567890",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "f2a3b4c5-d6e7-8901-mnop-012345678901",
"role": "admin",
"status": "accepted",
"invited_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2026-01-10T08:00:00Z"
}
}
/api/v1/projects/:id/members/:mid
Removes a member from the project, revoking all access permissions.
curl -X DELETE "https://jobcelis.com/api/v1/projects/PROJECT_ID/members/MEMBER_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"ok": true
}
/api/v1/invitations/pending
Lists the pending invitations for the authenticated user across all projects.
curl "https://jobcelis.com/api/v1/invitations/pending" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"data": [
{
"id": "f3a4b5c6-d7e8-9012-opqr-345678901234",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "f2a3b4c5-d6e7-8901-mnop-012345678901",
"role": "member",
"status": "pending",
"invited_by": "c3d4e5f6-a7b8-9012-cdef-234567890abc",
"inserted_at": "2026-01-20T09:00:00Z"
}
]
}
/api/v1/invitations/:id/accept
Accepts a pending invitation, granting access to the project with the assigned role.
curl -X POST "https://jobcelis.com/api/v1/invitations/INVITATION_ID/accept" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"data": {
"id": "f3a4b5c6-d7e8-9012-opqr-345678901234",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "f2a3b4c5-d6e7-8901-mnop-012345678901",
"role": "member",
"status": "accepted",
"invited_by": "c3d4e5f6-a7b8-9012-cdef-234567890abc",
"inserted_at": "2026-01-20T09:00:00Z"
}
}
/api/v1/invitations/:id/reject
Rejects a pending invitation, declining access to the project.
curl -X POST "https://jobcelis.com/api/v1/invitations/INVITATION_ID/reject" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"data": {
"id": "f3a4b5c6-d7e8-9012-opqr-345678901234",
"project_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "f2a3b4c5-d6e7-8901-mnop-012345678901",
"role": "member",
"status": "rejected",
"invited_by": "c3d4e5f6-a7b8-9012-cdef-234567890abc",
"inserted_at": "2026-01-20T09:00:00Z"
}
}
Sandbox
Testing endpoints for webhook verification and integration.
Create isolated sandbox endpoints to test webhook integrations without requiring external infrastructure. Each endpoint provides a unique URL that captures and logs all incoming HTTP requests.
/api/v1/sandbox-endpoints
Lists sandbox endpoints created for webhook testing.
curl "https://jobcelis.com/api/v1/sandbox-endpoints" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"slug": "test-endpoint-x7k9",
"name": "Test Endpoint",
"url": "/sandbox/test-endpoint-x7k9",
"expires_at": "2026-03-14T14:30:00Z",
"inserted_at": "2026-03-07T14:30:00Z"
}
]
}
/api/v1/sandbox-endpoints
Creates a new sandbox endpoint for testing and request inspection.
curl -X POST "https://jobcelis.com/api/v1/sandbox-endpoints" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Test Endpoint"}'
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"slug": "test-endpoint-x7k9",
"name": "Test Endpoint",
"url": "/sandbox/test-endpoint-x7k9",
"expires_at": "2026-03-14T14:30:00Z",
"inserted_at": "2026-03-07T14:30:00Z"
}
/api/v1/sandbox-endpoints/:id/requests
Retrieves all HTTP requests received by a sandbox endpoint.
curl "https://jobcelis.com/api/v1/sandbox-endpoints/ENDPOINT_ID/requests" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"method": "POST",
"path": "/sandbox/test-endpoint-x7k9",
"headers": {"content-type": "application/json"},
"body": {"order_id": "123", "amount": 99.99},
"query_params": {},
"ip": "203.0.113.42",
"inserted_at": "2026-03-07T14:30:00Z"
}
]
}
/api/v1/sandbox-endpoints/:id
Deletes a sandbox endpoint and discards all captured data.
curl -X DELETE "https://jobcelis.com/api/v1/sandbox-endpoints/ENDPOINT_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"status": "deleted"
}
Analytics
Project performance metrics and visualizations.
/api/v1/analytics/events-per-day
Daily event volume.
curl "https://jobcelis.com/api/v1/analytics/events-per-day" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{"date": "2026-03-05", "count": 245},
{"date": "2026-03-06", "count": 312},
{"date": "2026-03-07", "count": 178}
]
}
/api/v1/analytics/deliveries-per-day
Daily delivery volume.
curl "https://jobcelis.com/api/v1/analytics/deliveries-per-day" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{"date": "2026-03-05", "count": 320},
{"date": "2026-03-06", "count": 415},
{"date": "2026-03-07", "count": 198}
]
}
/api/v1/analytics/top-topics
Top event topics by volume.
curl "https://jobcelis.com/api/v1/analytics/top-topics" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{"topic": "order.created", "count": 1250},
{"topic": "user.signup", "count": 890},
{"topic": "payment.completed", "count": 567}
]
}
/api/v1/analytics/webhook-stats
Webhook performance statistics.
curl "https://jobcelis.com/api/v1/analytics/webhook-stats" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"webhook_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://example.com/hook",
"total_deliveries": 150,
"successful": 142,
"failed": 8
}
]
}
Audit Log
Immutable action log for the project.
/api/v1/audit-log
Retrieves audit log entries with pagination support.
curl "https://jobcelis.com/api/v1/audit-log?limit=20" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "webhook.created",
"resource_type": "webhook",
"resource_id": "f1e2d3c4-b5a6-7890-fedc-ba0987654321",
"metadata": {"url": "https://example.com/hook"},
"user_id": "c3d4e5f6-a7b8-9012-cdef-234567890abc",
"ip_address": "203.0.113.42",
"inserted_at": "2026-03-07T14:30:00Z"
}
]
}
Optional filters: action, actor_email, from, to.
Real-time Streaming
Receive events and delivery updates in real time via SSE (Server-Sent Events) or WebSocket (Phoenix Channel). Two protocols, same data.
Message types
Both protocols (SSE and WebSocket) transmit the same message types:
| Message | When it fires | Fields |
|---|---|---|
| event.created | Every time a new event arrives at the project | id, topic, payload, occurred_at |
| delivery.updated | Every time a webhook responds (success or failure) | id, status, event_id |
| connected | When the connection is established (SSE only) | project_id |
| keepalive | Every 30 seconds to keep the connection alive (SSE only) | — |
SSE vs WebSocket
| SSE | WebSocket | |
|---|---|---|
| Endpoint | GET /api/v1/stream | WS /ws |
| Authentication | Header Authorization / X-Api-Key | Query param ?token=API_KEY |
| Address | Unidirectional (server → client) | Bidirectional |
| Reconnection | Automatic in browsers (EventSource) | Automatic with Phoenix Socket |
| Ideal for | curl, scripts, CLIs, simple integrations | Frontend apps, dashboards, persistent services |
| Scope | events:read | events:read |
| Timeout | 120 seconds of inactivity (reconnect) | 120 seconds of inactivity (reconnect) |
SSE (Server-Sent Events)
/api/v1/stream
Establishes an SSE connection to receive events and delivery updates in real time. The connection stays open and transmits data as it occurs.
curl -N "https://jobcelis.com/api/v1/stream" \
-H "Authorization: Bearer YOUR_TOKEN"
Server-Sent Events stream.
data: {"type":"connected","project_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"}
data: {"type":"event.created","data":{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","topic":"order.created","payload":{"order_id":"123"},"occurred_at":"2026-03-07T14:30:00Z"}}
data: {"type":"delivery.updated","data":{"id":"c3d4e5f6-a7b8-9012-cdef-234567890abc","status":"delivered","event_id":"b2c3d4e5-f6a7-8901-bcde-f12345678901"}}
: keepalive
JavaScript (browser or Node.js)
const source = new EventSource(
"https://jobcelis.com/api/v1/stream",
{ headers: { "Authorization": "Bearer YOUR_TOKEN" } }
);
source.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === "event.created") {
console.log("New event:", data.data.topic, data.data.payload);
}
if (data.type === "delivery.updated") {
console.log("Delivery:", data.data.id, "→", data.data.status);
}
};
source.onerror = () => console.log("Reconnecting...");
Python
import requests, json
response = requests.get(
"https://jobcelis.com/api/v1/stream",
headers={"Authorization": "Bearer YOUR_TOKEN"},
stream=True
)
for line in response.iter_lines():
if line and line.startswith(b"data: "):
event = json.loads(line[6:])
print(f"{event['type']}: {event.get('data', {})}")
WebSocket (Phoenix Channel)
Persistent connection via WebSocket using Phoenix Channel. Authentication with API Key as query param. The channel is events:<project_id>.
Endpoint
Channel
JavaScript with Phoenix Socket
import { Socket } from "phoenix";
const socket = new Socket("wss://jobcelis.com/ws", {
params: { token: "YOUR_TOKEN" }
});
socket.connect();
const channel = socket.channel("events:YOUR_PROJECT_ID", {});
channel.join()
.receive("ok", () => console.log("Connected to stream"))
.receive("error", (resp) => console.log("Error:", resp));
// Listen for new events
channel.on("event:created", (event) => {
console.log("New event:", event.topic, event.payload);
});
// Listen for delivery updates
channel.on("delivery:updated", (delivery) => {
console.log("Delivery", delivery.id, "→", delivery.status);
});
Install phoenix with: npm install phoenix. The package provides the Socket client with automatic reconnection.
Use cases
Terminal monitor
Run curl -N in a terminal to watch events flowing in real time while developing. Ideal for debugging.
Microservice without webhook
A service connects to the stream and processes events without needing to expose a public URL. No firewall, no DNS.
Real-time dashboard
Show live activity in your own interface. Events appear instantly without reloading the page.
Instant alerts
React instantly when a delivery fails. Listen to delivery.updated and filter by status to send alerts to Slack, email or another channel.
Topic Wildcards
Wildcard patterns with * for filtering multiple webhook topics.
Webhook topics support pattern matching with wildcards. The pattern order.* matches order.created, order.updated, order.deleted, and similar topics under that namespace.
| Pattern | Matches |
|---|---|
| order.* | order.created, order.updated |
| *.created | order.created, user.created |
| * | All topics |
Delayed Events
Event scheduling for deferred delivery.
Include the deliver_at field with a future ISO 8601 timestamp. The event is persisted immediately but delivery is deferred until the specified timestamp.
curl -X POST "https://jobcelis.com/api/v1/events" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"reminder","deliver_at":"2026-12-25T00:00:00Z","message":"Merry Christmas"}'
Batch Events
Multiple event submission in a single API request.
/api/v1/events/batch
Submits an array of event objects in a single request.
curl -X POST "https://jobcelis.com/api/v1/events/batch" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"events":[{"topic":"a","data":1},{"topic":"b","data":2}]}'
{
"accepted": 2,
"rejected": 0,
"events": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "a",
"payload": 1,
"status": "pending",
"occurred_at": "2026-03-07T14:30:00Z",
"payload_hash": "e3b0c44298fc1c14...",
"inserted_at": "2026-03-07T14:30:00Z"
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"topic": "b",
"payload": 2,
"status": "pending",
"occurred_at": "2026-03-07T14:30:00Z",
"payload_hash": "a1b2c3d4e5f67890...",
"inserted_at": "2026-03-07T14:30:00Z"
}
]
}
Cursor Pagination
Efficient cursor-based pagination for large result sets.
Paginated endpoints use cursor-based pagination. Use the next_cursor value from the response as the cursor parameter in subsequent requests.
curl "https://jobcelis.com/api/v1/events?limit=20&cursor=NEXT_CURSOR" \
-H "Authorization: Bearer YOUR_TOKEN"
Webhook Templates
Pre-configured webhook templates.
/api/v1/webhooks/templates
Lists available webhook templates.
curl "https://jobcelis.com/api/v1/webhooks/templates" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"templates": [
{
"name": "Slack Notification",
"url": "https://hooks.slack.com/services/...",
"topics": ["order.created", "payment.completed"],
"headers": {"Content-Type": "application/json"}
},
{
"name": "Email Alert",
"url": "https://api.example.com/email-hook",
"topics": ["user.signup"],
"headers": {}
}
]
}
IP Allowlist
API access restriction via IP allowlisting.
Configure IP allowlisting for the project API key. Only requests from authorized IPs are accepted. Configured via project settings.
Simulator
Webhook validation without submitting real events.
/api/v1/simulate
Simulates event delivery to validate webhook configuration.
curl -X POST "https://jobcelis.com/api/v1/simulate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"topic":"order.created","payload":{"test":true}}'
{
"simulation": true,
"matching_webhooks": 1,
"results": [
{
"id": "b3e7c8a1-4f2d-4e9a-8c1b-5d6f7a8b9c0d",
"url": "https://example.com/hook",
"topics": ["order.*"]
}
]
}
Idempotency Keys
Prevents duplicate event processing using client-provided unique keys.
Send idempotency_key in the body or the X-Idempotency-Key header to deduplicate events. If an event with the same key already exists in the project, the existing event is returned without creating a new one.
# Option 1: idempotency_key in the request body
curl -X POST "https://jobcelis.com/api/v1/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"topic":"order.created","amount":150,"idempotency_key":"order-123-abc"}'
# Option 2: X-Idempotency-Key as header
curl -X POST "https://jobcelis.com/api/v1/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: order-123-abc" \
-d '{"topic":"order.created","amount":150}'
External alerts
Receive notifications outside the dashboard when something fails with your webhooks.
Configure notification channels to receive alerts via email, Slack, Discord, or meta-webhook when critical events occur.
| Channel | Settings |
|---|---|
| Recipient email address | |
| slack | Slack Incoming Webhook URL |
| discord | Discord Webhook URL |
| webhook | URL of an HTTP endpoint that receives alerts (meta-webhook) |
/api/v1/notification-channels
Create or update a notification channel.
curl -X PUT "https://jobcelis.com/api/v1/notification-channels" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"channel":"slack","config":{"webhook_url":"https://hooks.slack.com/services/..."},"events":["webhook_failing","circuit_open"]}'
/api/v1/notification-channels/test
Send a test notification to the configured channel.
curl -X POST "https://jobcelis.com/api/v1/notification-channels/test" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"channel":"slack"}'
/api/v1/notification-channels
List the configured notification channels.
curl "https://jobcelis.com/api/v1/notification-channels" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"project_id": "p1a2b3c4-d5e6-7890-abcd-ef1234567890",
"email_enabled": true,
"email_address": "alerts@example.com",
"slack_enabled": true,
"slack_webhook_url": "https://hooks.slack.com/••••••",
"discord_enabled": false,
"discord_webhook_url": null,
"meta_webhook_enabled": false,
"meta_webhook_url": null,
"meta_webhook_secret": null,
"event_types": ["webhook_failing", "circuit_open"],
"inserted_at": "2026-03-07T14:30:00Z",
"updated_at": "2026-03-07T14:30:00Z"
}
}
/api/v1/notification-channels
Delete the notification channel configuration.
curl -X DELETE "https://jobcelis.com/api/v1/notification-channels" \
-H "Authorization: Bearer YOUR_API_KEY"
# 204 No Content (empty response body)
Embeddable Portal
JavaScript widget for your end-users to manage their own webhooks.
Generate an embed token with specific scopes and use the JS widget so your clients can configure webhooks and view deliveries without accessing your dashboard.
/api/v1/embed/tokens
Generate a new embed token.
curl -X POST "https://jobcelis.com/api/v1/embed/tokens" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id":"PROJECT_ID","name":"My Portal"}'
/api/v1/embed/tokens
List the embed tokens for the project.
curl "https://jobcelis.com/api/v1/embed/tokens" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"prefix": "emb_abc",
"name": "My Portal",
"status": "active",
"scopes": ["webhooks:read", "webhooks:write"],
"allowed_origins": ["https://example.com"],
"metadata": {},
"expires_at": null,
"inserted_at": "2026-03-07T14:30:00Z"
}
]
}
/api/v1/embed/tokens/:id
Revoke an embed token.
curl -X DELETE "https://jobcelis.com/api/v1/embed/tokens/TOKEN_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"status": "revoked"
}
Integrate the widget
<script src="https://jobcelis.com/embed.js"></script>
<div id="jobcelis-portal"></div>
<script>
JobcelisPortal.init({
token: "emb_...",
container: "#jobcelis-portal",
baseUrl: "https://jobcelis.com",
locale: "en"
});
</script>
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/embed/webhooks | List webhooks |
| POST | /api/v1/embed/webhooks | Create webhook |
| GET | /api/v1/embed/deliveries | List deliveries |
| POST | /api/v1/embed/deliveries/:id/retry | Retry delivery |
Outbound Rate Limiting
Control webhook delivery speed to avoid overwhelming receiver servers.
Each webhook can have its own rate limit. If not configured, default values apply.
curl -X PATCH "https://jobcelis.com/api/v1/webhooks/WEBHOOK_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"rate_limit":{"max_per_second":100,"max_per_minute":5000}}'
Prometheus Metrics
Prometheus-compatible /metrics endpoint for professional monitoring with Grafana.
Available metrics:
Counters
-
jobcelis_events_created_total— Events created (by project, topic) -
jobcelis_deliveries_success_total— Successful deliveries (by project, webhook, topic) -
jobcelis_deliveries_failed_total— Failed deliveries (by project, webhook, topic)
Gauges
-
jobcelis_webhooks_active— Active webhooks -
jobcelis_circuit_breakers_open— Open circuit breakers -
jobcelis_deliveries_pending— Pending deliveries in queue
Histograms
-
jobcelis_delivery_latency_milliseconds— Delivery latency (by project, webhook, topic)
Webhook testing
Send a test event to a webhook without creating a real event in the project.
Use the test endpoint to verify that the receiver is configured correctly. A test payload with type webhook.test is sent, the HMAC signature is computed if the webhook has a secret, and the response code and latency are returned.
/api/v1/webhooks/:id/test
Send a test event to a webhook.
curl -X POST https://jobcelis.com/api/v1/webhooks/WEBHOOK_ID/test \
-H "Authorization: Bearer YOUR_TOKEN"
{
"success": true,
"status": 200,
"latency_ms": 145,
"webhook_id": "abc-123"
}
Data retention
Configure automatic retention per project and execute selective manual purges.
Automatic retention
Configure how many days to retain each data type. The system automatically purges old records weekly. A value of 0 means unlimited retention.
/api/v1/retention
View the current retention policy for the project.
curl "https://jobcelis.com/api/v1/retention" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"retention_days": null,
"retention_policy": {
"events_days": 90,
"deliveries_days": 30,
"audit_logs_days": 365
}
}
/api/v1/retention
Update retention policy.
curl -X PATCH https://jobcelis.com/api/v1/retention \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"events_days": 90, "deliveries_days": 30, "audit_logs_days": 365}'
{
"retention_policy": {
"events_days": 90,
"deliveries_days": 30,
"audit_logs_days": 365
}
}
Manual purge
Delete data selectively by type, date, topic, and status. Use the preview endpoint to see how many records would be deleted before executing.
/api/v1/purge/preview
Purge preview (without deleting).
curl -X POST https://jobcelis.com/api/v1/purge/preview \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "deliveries", "older_than": "2025-01-01", "status": "failed"}'
{
"type": "deliveries",
"count": 1247,
"older_than": "2025-01-01"
}
/api/v1/purge
Execute data purge.
curl -X POST https://jobcelis.com/api/v1/purge \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "deliveries", "older_than": "2025-01-01", "status": "failed"}'
{
"type": "deliveries",
"deleted_count": 1247,
"older_than": "2025-01-01"
}
Available purge types
| Type | Optional filters |
|---|---|
| events | topic |
| deliveries | topic, status |
| audit_logs | — |
| dead_letters | — |
SDKs (12 languages)
Official SDKs for major programming languages.
All SDKs provide complete API coverage. Select the preferred language:
Installation links
Send an event
# Install npm install @jobcelis/sdk # Usage const { JobcelisClient } = require('@jobcelis/sdk'); const client = new JobcelisClient({ apiKey: 'YOUR_API_KEY' }); await client.sendEvent({ topic: 'order.created', payload: { order_id: '12345', amount: 99.99 }, });
# Install pip install jobcelis # Usage from jobcelis import JobcelisClient client = JobcelisClient(api_key="YOUR_API_KEY") client.send_event("order.created", {"order_id": "12345", "amount": 99.99})
# Install go get github.com/vladimirCeli/go-jobcelis # Usage client := jobcelis.NewClient("YOUR_API_KEY") resp, err := client.SendEvent(ctx, jobcelis.EventRequest{ Topic: "order.created", Payload: map[string]interface{}{"order_id": "12345", "amount": 99.99}, })
# Install composer require jobcelis/sdk # Usage use Jobcelis\JobcelisClient; $client = new JobcelisClient('YOUR_API_KEY'); $response = $client->sendEvent([ 'topic' => 'order.created', 'payload' => ['order_id' => '12345', 'amount' => 99.99], ]);
# Install gem install jobcelis # Usage require 'jobcelis' client = Jobcelis::Client.new(api_key: 'YOUR_API_KEY') response = client.send_event( topic: 'order.created', payload: { order_id: '12345', amount: 99.99 } )
# Install {:jobcelis, "~> 1.0"} # add to mix.exs deps # Usage client = Jobcelis.client(api_key: "YOUR_API_KEY") {:ok, event} = Jobcelis.send_event(client, topic: "order.created", payload: %{order_id: "12345", amount: 99.99} )
# Install dotnet add package Jobcelis # Usage using Jobcelis; var client = new JobcelisClient("YOUR_API_KEY"); var response = await client.SendEventAsync(new { topic = "order.created", payload = new { order_id = "12345", amount = 99.99 } });
# Install cargo add jobcelis # Usage use jobcelis::JobcelisClient; let client = JobcelisClient::new("YOUR_API_KEY"); let response = client.send_event( "order.created", serde_json::json!({"order_id": "12345", "amount": 99.99}) ).await?;
# Install // Swift Package Manager .package(url: "https://github.com/vladimirCeli/jobcelis-swift", from: "1.0.0") # Usage import Jobcelis let client = JobcelisClient(apiKey: "YOUR_API_KEY") let response = try await client.sendEvent( topic: "order.created", payload: ["order_id": "12345", "amount": 99.99] )
# Install <!-- Maven --> <dependency> <groupId>com.jobcelis</groupId> <artifactId>jobcelis</artifactId> <version>1.0.0</version> </dependency> # Usage import com.jobcelis.JobcelisClient; JobcelisClient client = new JobcelisClient("YOUR_API_KEY"); JsonObject response = client.sendEvent( "order.created", Map.of("order_id", "12345", "amount", 99.99) );
# Install dart pub add jobcelis # Usage import 'package:jobcelis/jobcelis.dart'; final client = JobcelisClient(apiKey: 'YOUR_API_KEY'); final response = await client.sendEvent( topic: 'order.created', payload: {'order_id': '12345', 'amount': 99.99}, );
# Install implementation("com.jobcelis:jobcelis:1.0.0") # Usage import com.jobcelis.JobcelisClient val client = JobcelisClient("YOUR_API_KEY") val response = client.sendEvent( topic = "order.created", payload = mapOf("order_id" to "12345", "amount" to 99.99) )
CLI
Command-line interface for managing events, webhooks, jobs, and platform resources.
@jobcelis/cli on npm
npm install -g @jobcelis/cli
export JOBCELIS_API_KEY="YOUR_API_KEY"
# Send an event
jobcelis events send --topic order.created --payload '{"id":"123"}'
# List events
jobcelis events list --limit 10
# List webhooks
jobcelis webhooks list
# Create a webhook
jobcelis webhooks create --url https://example.com/hook --topics "order.*"
# Check platform status
jobcelis status
Webhook Signature Verification
HMAC signature verification of each delivery to confirm authenticity.
Each delivery includes an HMAC signature in the X-Signature header. Always verify signatures to confirm the request originated from Jobcelis.
Signature algorithm
- Compute HMAC-SHA256 of the raw body using the webhook secret
- Encode the result in Base64 without padding
- Sent in the header as: X-Signature: sha256=<base64>
Verification function by language
# Install npm install @jobcelis/sdk # Usage const crypto = require('crypto'); function verifySignature(secret, body, signature) { if (!signature.startsWith('sha256=')) return false; const received = signature.slice(7); const expected = crypto .createHmac('sha256', secret) .update(body) .digest('base64') .replace(/=+$/, ''); const a = Buffer.from(received, 'base64'); const b = Buffer.from(expected, 'base64'); if (a.length !== b.length) return false; return crypto.timingSafeEqual(a, b); }
# Install pip install jobcelis # Usage import base64, hashlib, hmac def verify_signature(secret, body, signature): if not signature.startswith('sha256='): return False received = signature[7:] expected = base64.b64encode( hmac.new(secret.encode(), body.encode(), hashlib.sha256).digest() ).rstrip(b'=').decode() return hmac.compare_digest(received, expected)
# Install go get github.com/vladimirCeli/go-jobcelis # Usage import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "strings" ) func VerifySignature(secret, body, signature string) bool { if !strings.HasPrefix(signature, "sha256=") { return false } received := signature[7:] mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(body)) expected := base64.RawStdEncoding.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(received), []byte(expected)) }
# Install composer require jobcelis/sdk # Usage <?php function verifySignature(string $secret, string $body, string $signature): bool { if (!str_starts_with($signature, 'sha256=')) return false; $received = substr($signature, 7); $expected = rtrim(base64_encode( hash_hmac('sha256', $body, $secret, true) ), '='); return hash_equals($received, $expected); }
# Install gem install jobcelis # Usage require 'openssl' require 'base64' def verify_signature(secret, body, signature) return false unless signature.start_with?('sha256=') received = signature[7..] expected = Base64.strict_encode64( OpenSSL::HMAC.digest('sha256', secret, body) ).delete_suffix('=') Rack::Utils.secure_compare(received, expected) end
# Install {:jobcelis, "~> 1.0"} # add to mix.exs deps # Usage defmodule WebhookVerifier do def verify_signature(secret, body, signature) do case signature do "sha256=" <> received -> expected = :crypto.mac(:hmac, :sha256, secret, body) |> Base.encode64(padding: false) Plug.Crypto.secure_compare(received, expected) _ -> false end end end
# Install dotnet add package Jobcelis # Usage using System.Security.Cryptography; using System.Text; public static bool VerifySignature(string secret, string body, string signature) { if (!signature.StartsWith("sha256=")) return false; var received = signature.Substring(7); using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body)); var expected = Convert.ToBase64String(hash).TrimEnd('='); return CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(received), Encoding.UTF8.GetBytes(expected)); }
# Install cargo add jobcelis # Usage use hmac::{Hmac, Mac}; use sha2::Sha256; use base64::engine::general_purpose::STANDARD_NO_PAD; use base64::Engine; fn verify_signature(secret: &str, body: &str, signature: &str) -> bool { let received = match signature.strip_prefix("sha256=") { Some(s) => s, None => return false, }; let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap(); mac.update(body.as_bytes()); let expected = STANDARD_NO_PAD.encode(mac.finalize().into_bytes()); received == expected }
# Install // Swift Package Manager .package(url: "https://github.com/vladimirCeli/jobcelis-swift", from: "1.0.0") # Usage import CryptoKit import Foundation func verifySignature(secret: String, body: String, signature: String) -> Bool { guard signature.hasPrefix("sha256=") else { return false } let received = String(signature.dropFirst(7)) let key = SymmetricKey(data: Data(secret.utf8)) let mac = HMAC<SHA256>.authenticationCode( for: Data(body.utf8), using: key ) let expected = Data(mac).base64EncodedString() .replacingOccurrences(of: "=", with: "") return received == expected }
# Install <!-- Maven --> <dependency> <groupId>com.jobcelis</groupId> <artifactId>jobcelis</artifactId> <version>1.0.0</version> </dependency> # Usage import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public static boolean verifySignature(String secret, String body, String signature) { if (!signature.startsWith("sha256=")) return false; String received = signature.substring(7); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")); byte[] hash = mac.doFinal(body.getBytes("UTF-8")); String expected = Base64.getEncoder().withoutPadding().encodeToString(hash); return MessageDigest.isEqual(received.getBytes(), expected.getBytes()); }
# Install dart pub add jobcelis # Usage import 'dart:convert'; import 'package:crypto/crypto.dart'; bool verifySignature(String secret, String body, String signature) { if (!signature.startsWith('sha256=')) return false; final received = signature.substring(7); final hmac = Hmac(sha256, utf8.encode(secret)); final digest = hmac.convert(utf8.encode(body)); final expected = base64Encode(digest.bytes).replaceAll('=', ''); return received == expected; }
# Install implementation("com.jobcelis:jobcelis:1.0.0") # Usage import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec import java.security.MessageDigest import java.util.Base64 fun verifySignature(secret: String, body: String, signature: String): Boolean { if (!signature.startsWith("sha256=")) return false val received = signature.removePrefix("sha256=") val mac = Mac.getInstance("HmacSHA256") mac.init(SecretKeySpec(secret.toByteArray(), "HmacSHA256")) val hash = mac.doFinal(body.toByteArray()) val expected = Base64.getEncoder().withoutPadding().encodeToString(hash) return MessageDigest.isEqual(received.toByteArray(), expected.toByteArray()) }
Framework middleware examples
Express.js (Node.js)
const crypto = require('crypto');
app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
const signature = req.headers['x-signature'];
const body = req.body.toString();
if (!verifySignature(process.env.WEBHOOK_SECRET, body, signature)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(body);
// Process event...
res.sendStatus(200);
});
FastAPI (Python)
from fastapi import FastAPI, Request, HTTPException
@app.post("/webhook")
async def webhook(request: Request):
body = (await request.body()).decode()
signature = request.headers.get("x-signature", "")
if not verify_signature(WEBHOOK_SECRET, body, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
event = await request.json()
# Process event...
return {"ok": True}
Gin (Go)
func webhookHandler(c *gin.Context) {
body, _ := io.ReadAll(c.Request.Body)
signature := c.GetHeader("X-Signature")
if !jobcelis.VerifyWebhookSignature(secret, string(body), signature) {
c.JSON(401, gin.H{"error": "invalid signature"})
return
}
// Process event...
c.JSON(200, gin.H{"ok": true})
}
Phoenix (Elixir)
defmodule MyAppWeb.WebhookController do
use MyAppWeb, :controller
def handle(conn, _params) do
{:ok, body, conn} = Plug.Conn.read_body(conn)
sig = Plug.Conn.get_req_header(conn, "x-signature") |> List.first("")
if Jobcelis.WebhookVerifier.verify(secret, body, sig) do
event = Jason.decode!(body)
# Process event...
json(conn, %{ok: true})
else
conn |> put_status(401) |> json(%{error: "invalid signature"})
end
end
end
Laravel (PHP)
Route::post('/webhook', function (Request $request) {
$body = $request->getContent();
$signature = $request->header('X-Signature', '');
if (!WebhookVerifier::verify($secret, $body, $signature)) {
return response()->json(['error' => 'invalid signature'], 401);
}
$event = json_decode($body, true);
// Process event...
return response()->json(['ok' => true]);
});
Spring Boot (Java)
@PostMapping("/webhook")
public ResponseEntity<Map<String, Object>> webhook(
@RequestBody String body,
@RequestHeader("X-Signature") String signature) {
if (!WebhookVerifier.verify(secret, body, signature)) {
return ResponseEntity.status(401).body(Map.of("error", "invalid signature"));
}
// Process event...
return ResponseEntity.ok(Map.of("ok", true));
}
ASP.NET (C#)
[HttpPost("webhook")]
public async Task<IActionResult> Webhook() {
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var signature = Request.Headers["X-Signature"].FirstOrDefault() ?? "";
if (!WebhookVerifier.Verify(secret, body, signature))
return Unauthorized(new { error = "invalid signature" });
// Process event...
return Ok(new { ok = true });
}
Rails (Ruby)
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
def handle
body = request.raw_post
signature = request.headers["X-Signature"] || ""
unless Jobcelis::WebhookVerifier.verify(
secret: ENV["WEBHOOK_SECRET"], body: body, signature: signature
)
return render json: { error: "invalid signature" }, status: 401
end
event = JSON.parse(body)
# Process event...
render json: { ok: true }
end
end
CLI verification
jobcelis verify-signature \
--secret "whsec_your_secret" \
--body '{"topic":"order.created","data":{"id":"123"}}' \
--signature "sha256=abc123..."
Account Lockout
Protection against brute force attacks.
After multiple failed authentication attempts within a short period, the account is temporarily locked. Provides protection against brute force attacks and compromised credentials.
Session Management
Active session control.
Web sessions use encrypted cookies with inactivity timeouts. Sessions automatically terminate after a period of inactivity.
MFA / TOTP
Two-factor authentication
Enable two-factor authentication from the account page. Use a compatible authenticator application (Google Authenticator, Authy, etc.) to scan the QR code. Each login requires the password and the TOTP code from the application.
Password Policy
Password requirements policy.
- Minimum length required
- Must include uppercase, lowercase and numbers
- Special characters recommended
- Passwords are stored with memory-hard secure hashing
Data Encryption
Personal data protection.
Personal data (email, name) is encrypted at rest with industry-standard encryption. Email lookups use deterministic hashing, avoiding the need to decrypt during queries.
Circuit Breaker
Automatic protection for unstable webhooks.
If a webhook fails repeatedly, the circuit breaker temporarily disables it to prevent failure cascades. When the endpoint recovers, the webhook is automatically re-enabled.
Breach Detection
Continuous security monitoring.
The system continuously monitors for anomalous patterns: multiple failed authentication attempts, access from unusual locations, and other indicators of potential security incidents.
Event Integrity
Event integrity and immutability guarantee.
Each event receives a unique cryptographic hash at creation time. Enables verification that content has not been altered. Events are stored immutably.
Additionally, each event supports an optional idempotency_key for duplicate prevention. If multiple events share the same idempotency_key, only the first is processed.
Monitoring
Automated platform monitoring.
The platform is continuously monitored. Current status is available on the status page (/status). Monitored components include the database, the processing system, and the cache layer.
Backups
Automatic backups.
Automated backups are performed periodically. Backups are stored securely and encrypted. In the event of an incident, data can be restored expeditiously.
GDPR / RGPD
Data protection rights.
Jobcelis is GDPR/RGPD compliant. Users have the following data protection rights:
- Access: Export all your personal data (GET /api/v1/me/data)
- Rectification: Update your profile from the account page
- Description Restrict processing (POST /api/v1/me/restrict)
- Objection: Object to processing (POST /api/v1/me/object)
- Portability: Export data in standard JSON format
/api/v1/me/data
Exports all your personal data stored on the platform (GDPR Article 15 — right of access).
curl "https://jobcelis.com/api/v1/me/data" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"data": {
"profile": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "user@example.com",
"name": "Jane Doe",
"role": "user",
"inserted_at": "2026-01-10T08:00:00Z"
},
"consents": [...],
"activity": [...]
}
}
/api/v1/me/restrict
Restricts the processing of your personal data (GDPR Article 18). Requires password confirmation.
curl -X POST "https://jobcelis.com/api/v1/me/restrict" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"password": "YOUR_PASSWORD"}'
{
"status": "restricted",
"restricted_at": "2026-03-10T12:00:00Z"
}
/api/v1/me/restrict
Lifts the data processing restriction, restoring active status.
curl -X DELETE "https://jobcelis.com/api/v1/me/restrict" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"status": "active"
}
/api/v1/me/object
Registers an objection to the processing of your personal data (GDPR Article 21).
curl -X POST "https://jobcelis.com/api/v1/me/object" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"processing_consent": false
}
/api/v1/me/object
Withdraws the processing objection, restoring processing consent.
curl -X DELETE "https://jobcelis.com/api/v1/me/object" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"processing_consent": true
}
Consents
Versioned GDPR consent management.
/api/v1/me/consents
Retrieves the current status of data processing consents.
curl "https://jobcelis.com/api/v1/me/consents" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"consents": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"purpose": "essential",
"version": 1,
"granted_at": "2026-01-15T10:00:00Z"
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"purpose": "analytics",
"version": 2,
"granted_at": "2026-02-20T14:00:00Z"
}
],
"outdated": ["analytics"],
"current_versions": {
"essential": 1,
"analytics": 3
}
}
/api/v1/me/consents/:purpose/accept
Accepts a specific consent by purpose identifier.
curl -X POST "https://jobcelis.com/api/v1/me/consents/analytics/accept" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"consent": {
"id": "c3d4e5f6-a7b8-9012-cdef-234567890abc",
"purpose": "analytics",
"version": 3,
"granted_at": "2026-03-07T14:30:00Z"
}
}
HTTP Status Codes
Standard HTTP status codes used by the API.
| Code | Meaning |
|---|---|
| 200 | OK |
| 201 | Created |
| 204 | No Content |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 409 | Conflict |
| 422 | Unprocessable Entity |
| 429 | Too Many Requests |
| 500 | Internal Server Error |
Error responses
Standard format for errors returned by the API.
Failed requests return a JSON response with an "error" field describing the issue.
Common error examples
// 400 Bad Request — missing or invalid parameters
{"error": "Missing required field: topic"}
// 401 Unauthorized — invalid or missing API key
{"error": "Invalid API key"}
// 404 Not Found — resource does not exist
{"error": "Event not found"}
// 422 Unprocessable Entity — validation error
{"error": "URL must start with https://"}
// 429 Too Many Requests — rate limit exceeded
{"error": "Rate limit exceeded. Try again later."}
Response Headers
Headers included in API responses.
| Header | Description |
|---|---|
| X-Request-Id | Unique request ID for debugging |
| X-RateLimit-Limit | Request limit per window |
| X-RateLimit-Remaining | Remaining requests in the window |
| X-RateLimit-Reset | Rate limit reset timestamp |
Health Check
Endpoint for external monitoring.
/health
Returns HTTP 200 if the platform is operational.
curl "https://jobcelis.com/health"
{
"status": "healthy",
"timestamp": "2026-03-07T14:30:00Z"
}
API Key Scopes
Granular permissions for API keys.
Configure scopes to restrict API key permissions. Available scopes:
events:read
— Read events
events:write
— Create account
webhooks:read
— Read webhooks
webhooks:write
— Create webhook
jobs:read
— Read jobs
jobs:write
— Create/edit jobs
admin
— Full access