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

FieldTypeDescription
idstringUnique identifier for this event (UUID)
event_typestringType of event (see Event types below)
timestampstringISO 8601 timestamp when the event occurred
project_idnumber or nullID of the project this event relates to
payloadobjectEvent-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

  1. Go to Projects and open your project.
  2. Click Settings, then select Webhooks.
  3. Click Add Webhook.
  4. Enter your endpoint URL, choose the events to subscribe to, and optionally add a secret for signature verification.
  5. Click Save.

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:

ParameterTypeDefaultMaxDescription
limitnumber50100Number 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

  1. Always verify signatures — never skip signature verification in production.
  2. Use HTTPS — only use HTTPS URLs for webhook endpoints.
  3. Validate event types — check that the event type is one you expect.
  4. Respond quickly — return a 2xx status code within 30 seconds.
  5. Process asynchronously — queue long-running tasks instead of processing them in the webhook handler.
  6. Handle retries idempotently — failed deliveries may be retried, so use the event_id to 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:

  1. Create a new RequestBin.
  2. Copy the generated URL.
  3. Create a webhook with that URL.
  4. Trigger an event in Temps.
  5. View the received payload in RequestBin.

Support

For questions or issues with webhooks:

Was this page helpful?