Webhooks
When an event fires in your project — a deployment succeeds, a domain is provisioned, a build fails — Temps delivers an HTTP POST to your endpoint within seconds. Configure a URL and choose your events; Temps handles signing, retries, and delivery logs.
Webhook event structure
All webhook events follow this base structure:
{
"id": "58fe24a7-3e21-451d-9210-33093e6f91cc",
"event_type": "deployment.succeeded",
"timestamp": "2025-11-24T18:38:10.000Z",
"project_id": 1,
"payload": {
// Event-specific data (see below)
}
}
Base fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for this event (UUID) |
event_type | string | Type of event (see Event types below) |
timestamp | string | ISO 8601 timestamp when the event occurred |
project_id | number or null | ID of the project this event relates to |
payload | object | Event-specific data (structure varies by event type) |
Event types
Deployment events
Events related to deployment lifecycle.
deployment.created
Triggered when a new deployment is initiated.
Payload structure:
{
deployment_id: number
project_id: number
project_name: string
environment: string
branch: string | null
commit_sha: string | null
commit_message: string | null
url: string | null
status: string
error_message: string | null
started_at: string | null // ISO 8601 timestamp
finished_at: string | null // ISO 8601 timestamp
}
Example:
{
"id": "58fe24a7-3e21-451d-9210-33093e6f91cc",
"event_type": "deployment.created",
"timestamp": "2025-11-24T18:35:00.000Z",
"project_id": 1,
"payload": {
"deployment_id": 123,
"project_id": 1,
"project_name": "my-app",
"environment": "production",
"branch": "main",
"commit_sha": "abc123def456",
"commit_message": "Deploy version 2.0",
"url": null,
"status": "pending",
"error_message": null,
"started_at": "2025-11-24T18:35:00.000Z",
"finished_at": null
}
}
deployment.succeeded
Triggered when a deployment completes successfully.
Payload structure: Same as deployment.created
Example:
{
"id": "58fe24a7-3e21-451d-9210-33093e6f91cc",
"event_type": "deployment.succeeded",
"timestamp": "2025-11-24T18:38:10.000Z",
"project_id": 1,
"payload": {
"deployment_id": 123,
"project_id": 1,
"project_name": "my-app",
"environment": "production",
"branch": "main",
"commit_sha": "abc123def456",
"commit_message": "Deploy version 2.0",
"url": "https://my-app.temps.dev",
"status": "succeeded",
"error_message": null,
"started_at": "2025-11-24T18:35:00.000Z",
"finished_at": "2025-11-24T18:38:10.000Z"
}
}
deployment.failed
Triggered when a deployment fails.
Payload structure: Same as deployment.created
Example:
{
"id": "58fe24a7-3e21-451d-9210-33093e6f91cc",
"event_type": "deployment.failed",
"timestamp": "2025-11-24T18:38:10.000Z",
"project_id": 1,
"payload": {
"deployment_id": 123,
"project_id": 1,
"project_name": "my-app",
"environment": "production",
"branch": "main",
"commit_sha": "abc123def456",
"commit_message": "Deploy version 2.0",
"url": null,
"status": "failed",
"error_message": "Build failed: npm run build exited with code 1",
"started_at": "2025-11-24T18:35:00.000Z",
"finished_at": "2025-11-24T18:38:10.000Z"
}
}
deployment.cancelled
Triggered when a deployment is cancelled by a user.
Payload structure: Same as deployment.created
deployment.ready
Triggered when a deployment is ready to receive traffic.
Payload structure: Same as deployment.created
Project events
Events related to project lifecycle.
project.created
Triggered when a new project is created.
Payload structure:
{
project_id: number
project_name: string
slug: string
repo_url: string | null
}
Example:
{
"id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"event_type": "project.created",
"timestamp": "2025-11-24T12:00:00.000Z",
"project_id": 1,
"payload": {
"project_id": 1,
"project_name": "My New Project",
"slug": "my-new-project",
"repo_url": "https://github.com/user/my-repo"
}
}
project.deleted
Triggered when a project is deleted.
Payload structure: Same as project.created
Domain events
Events related to custom domains and SSL provisioning.
domain.created
Triggered when a new domain is added to a project.
Payload structure:
{
domain_id: number
domain_name: string
project_id: number
project_name: string
is_primary: boolean
ssl_status: string | null
}
Example:
{
"id": "7f6e5d4c-3b2a-1098-7654-3210fedcba98",
"event_type": "domain.created",
"timestamp": "2025-11-24T14:30:00.000Z",
"project_id": 1,
"payload": {
"domain_id": 5,
"domain_name": "example.com",
"project_id": 1,
"project_name": "my-app",
"is_primary": true,
"ssl_status": "pending"
}
}
domain.provisioned
Triggered when SSL is provisioned for a domain (via Let's Encrypt).
Payload structure: Same as domain.created
Example:
{
"id": "7f6e5d4c-3b2a-1098-7654-3210fedcba98",
"event_type": "domain.provisioned",
"timestamp": "2025-11-24T14:35:00.000Z",
"project_id": 1,
"payload": {
"domain_id": 5,
"domain_name": "example.com",
"project_id": 1,
"project_name": "my-app",
"is_primary": true,
"ssl_status": "provisioned"
}
}
Configure webhooks
Dashboard
- Go to Projects and open your project.
- Click Settings, then select Webhooks.
- Click Add Webhook.
- Enter your endpoint URL, choose the events to subscribe to, and optionally add a secret for signature verification.
- Click Save.
The CLI does not yet support webhook management. Use the dashboard or API endpoints below.
API endpoints
List available event types
Get a list of all available webhook event types.
GET https://your-temps-instance.com/api/webhook-event-types
Response:
[
{
"event_type": "deployment.created",
"description": "Triggered when a new deployment is initiated",
"category": "Deployment"
},
{
"event_type": "deployment.succeeded",
"description": "Triggered when a deployment completes successfully",
"category": "Deployment"
}
]
Create a webhook
Create a new webhook for a project.
POST https://your-temps-instance.com/api/projects/{project_id}/webhooks
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
Request body:
{
"url": "https://example.com/webhook",
"secret": "your-secret-key",
"events": [
"deployment.created",
"deployment.succeeded",
"deployment.failed"
],
"enabled": true
}
Response (201 Created):
{
"id": 1,
"project_id": 1,
"url": "https://example.com/webhook",
"events": [
"deployment.created",
"deployment.succeeded",
"deployment.failed"
],
"enabled": true,
"has_secret": true,
"created_at": "2025-11-24T12:00:00.000Z",
"updated_at": "2025-11-24T12:00:00.000Z"
}
List webhooks
List all webhooks for a project.
GET https://your-temps-instance.com/api/projects/{project_id}/webhooks
Authorization: Bearer YOUR_TOKEN
Response (200 OK):
[
{
"id": 1,
"project_id": 1,
"url": "https://example.com/webhook",
"events": ["deployment.succeeded"],
"enabled": true,
"has_secret": true,
"created_at": "2025-11-24T12:00:00.000Z",
"updated_at": "2025-11-24T12:00:00.000Z"
}
]
Get a webhook
Get details of a specific webhook.
GET https://your-temps-instance.com/api/projects/{project_id}/webhooks/{webhook_id}
Authorization: Bearer YOUR_TOKEN
Response (200 OK):
{
"id": 1,
"project_id": 1,
"url": "https://example.com/webhook",
"events": ["deployment.succeeded"],
"enabled": true,
"has_secret": true,
"created_at": "2025-11-24T12:00:00.000Z",
"updated_at": "2025-11-24T12:00:00.000Z"
}
Update a webhook
Update an existing webhook.
PUT https://your-temps-instance.com/api/projects/{project_id}/webhooks/{webhook_id}
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
Request body (all fields optional):
{
"url": "https://example.com/new-webhook",
"secret": "new-secret-key",
"events": ["deployment.succeeded", "deployment.failed"],
"enabled": false
}
Response (200 OK):
{
"id": 1,
"project_id": 1,
"url": "https://example.com/new-webhook",
"events": ["deployment.succeeded", "deployment.failed"],
"enabled": false,
"has_secret": true,
"created_at": "2025-11-24T12:00:00.000Z",
"updated_at": "2025-11-24T12:15:00.000Z"
}
Delete a webhook
Delete a webhook.
DELETE https://your-temps-instance.com/api/projects/{project_id}/webhooks/{webhook_id}
Authorization: Bearer YOUR_TOKEN
Response (204 No Content)
List webhook deliveries
Get delivery history for a webhook.
GET https://your-temps-instance.com/api/projects/{project_id}/webhooks/{webhook_id}/deliveries?limit=50
Authorization: Bearer YOUR_TOKEN
Query parameters:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
limit | number | 50 | 100 | Number of deliveries to return |
Response (200 OK):
[
{
"id": 3,
"webhook_id": 1,
"event_type": "deployment.succeeded",
"event_id": "58fe24a7-3e21-451d-9210-33093e6f91cc",
"payload": "{\"id\":\"58fe24a7...\",\"event_type\":\"deployment.succeeded\",...}",
"success": true,
"status_code": 200,
"response_body": "{\"received\":true}",
"error_message": null,
"attempt_number": 1,
"created_at": "2025-11-24T18:38:10.000Z",
"delivered_at": "2025-11-24T18:38:10.000Z"
}
]
Get a webhook delivery
Get details of a specific webhook delivery, including the full payload that was sent.
GET https://your-temps-instance.com/api/projects/{project_id}/webhooks/{webhook_id}/deliveries/{delivery_id}
Authorization: Bearer YOUR_TOKEN
Response (200 OK):
{
"id": 3,
"webhook_id": 1,
"event_type": "deployment.succeeded",
"event_id": "58fe24a7-3e21-451d-9210-33093e6f91cc",
"payload": "{\"id\":\"58fe24a7-3e21-451d-9210-33093e6f91cc\",\"event_type\":\"deployment.succeeded\",\"timestamp\":\"2025-11-24T18:38:10.000Z\",\"project_id\":1,\"payload\":{\"deployment_id\":123,\"project_id\":1,\"project_name\":\"my-app\",\"environment\":\"production\",\"branch\":\"main\",\"commit_sha\":\"abc123\",\"commit_message\":\"Deploy v2.0\",\"url\":\"https://my-app.temps.dev\",\"status\":\"succeeded\",\"error_message\":null,\"started_at\":\"2025-11-24T18:35:00.000Z\",\"finished_at\":\"2025-11-24T18:38:10.000Z\"}}",
"success": true,
"status_code": 200,
"response_body": "{\"received\":true}",
"error_message": null,
"attempt_number": 1,
"created_at": "2025-11-24T18:38:10.000Z",
"delivered_at": "2025-11-24T18:38:10.000Z"
}
Retry a failed delivery
Retry a failed webhook delivery.
POST https://your-temps-instance.com/api/projects/{project_id}/webhooks/{webhook_id}/deliveries/{delivery_id}/retry
Authorization: Bearer YOUR_TOKEN
Response (200 OK):
{
"success": true,
"status_code": 200,
"error_message": null,
"attempt_number": 2
}
Security
HMAC signature verification
All webhook deliveries include an HMAC-SHA256 signature for verification. The signature is sent in the X-Webhook-Signature header.
Headers sent:
POST /your-webhook-endpoint
Content-Type: application/json
X-Webhook-Event: deployment.succeeded
X-Webhook-Delivery: 3
X-Webhook-Timestamp: 1732471090
X-Webhook-Signature: sha256=a1b2c3d4e5f6...
Verifying the signature (Node.js example):
const crypto = require('crypto');
function verifyWebhookSignature(payload, timestamp, signature, secret) {
const message = `${timestamp}.${payload}`;
const hmac = crypto.createHmac('sha256', secret);
hmac.update(message);
const expectedSignature = `sha256=${hmac.digest('hex')}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// In your webhook handler
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const payload = JSON.stringify(req.body);
const secret = 'your-webhook-secret';
if (!verifyWebhookSignature(payload, timestamp, signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
console.log('Valid webhook received:', req.body);
res.status(200).send('OK');
});
Verifying the signature (Python example):
import hmac
import hashlib
def verify_webhook_signature(payload: str, timestamp: str, signature: str, secret: str) -> bool:
message = f"{timestamp}.{payload}"
expected_signature = "sha256=" + hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
# In your webhook handler (Flask example)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature')
timestamp = request.headers.get('X-Webhook-Timestamp')
payload = request.get_data(as_text=True)
secret = 'your-webhook-secret'
if not verify_webhook_signature(payload, timestamp, signature, secret):
return 'Invalid signature', 401
# Process the webhook
data = request.get_json()
print(f"Valid webhook received: {data}")
return 'OK', 200
Best practices
- Always verify signatures — never skip signature verification in production.
- Use HTTPS — only use HTTPS URLs for webhook endpoints.
- Validate event types — check that the event type is one you expect.
- Respond quickly — return a 2xx status code within 30 seconds.
- Process asynchronously — queue long-running tasks instead of processing them in the webhook handler.
- Handle retries idempotently — failed deliveries may be retried, so use the
event_idto deduplicate.
Testing webhooks
Error messages
When a webhook delivery fails, you'll receive detailed error messages:
Connection errors:
Connection failed to http://localhost:3020/test. The service may not be running or the URL is incorrect. Please verify the endpoint is accessible and listening on the correct port.
Timeout errors:
Request timeout after 30 seconds. The endpoint at https://example.com/webhook did not respond in time. Check if the service is running and responding quickly enough.
HTTP errors:
HTTP 500 error from https://example.com/webhook: Internal Server Error. The endpoint rejected the webhook request.
Local testing with ngrok
For local development, use ngrok to expose your local server:
# Start your local webhook server
node server.js # or python app.py, etc.
# In another terminal, start ngrok
ngrok http 3000
# Use the ngrok URL in your webhook configuration
# e.g., https://abc123.ngrok.io/webhook
Testing with RequestBin
Use RequestBin or Webhook.site to inspect webhook payloads:
- Create a new RequestBin.
- Copy the generated URL.
- Create a webhook with that URL.
- Trigger an event in Temps.
- View the received payload in RequestBin.
Support
For questions or issues with webhooks:
- Review delivery logs in the Temps dashboard
- Join the Temps Discord community for help