Triggers
Triggers allow you to automatically execute actions when specific events occur in a collection. Triggers can make HTTP requests to external services when records are created, updated, or deleted.
Understanding Triggers
Section titled “Understanding Triggers”Triggers are configured at the collection level and automatically fire when:
- A new record is created
- An existing record is updated
- A record is deleted
Triggers send HTTP requests (webhooks) to configured URLs with event details.
Configuring Triggers
Section titled “Configuring Triggers”Triggers live in the collection’s options block in the IaC YAML (see
IaC); vski apply converges them like any other collection
setting.
Creating a Collection with Triggers
Section titled “Creating a Collection with Triggers”version: 1database: default
collections: notifications: type: base fields: - {name: title, type: text, required: true} - {name: message, type: text} - {name: status, type: select, options: {values: [active, inactive]}} options: triggers: - action: create url: https://api.example.com/webhook method: POST headers: Content-Type: application/json X-Webhook-Source: vski - action: update url: https://api.example.com/webhook method: PATCH - action: delete url: https://api.example.com/webhook method: DELETETrigger Types
Section titled “Trigger Types”Create Trigger
Section titled “Create Trigger”Fires when a new record is created:
- action: create url: https://api.example.com/new-record method: POST headers: Content-Type: application/json Authorization: "Bearer your-token"Update Trigger
Section titled “Update Trigger”Fires when a record is updated:
- action: update url: https://api.example.com/updated-record method: PATCHDelete Trigger
Section titled “Delete Trigger”Fires when a record is deleted:
- action: delete url: https://api.example.com/deleted-record method: DELETETrigger Payload
Section titled “Trigger Payload”When a trigger fires, it sends a JSON payload to the configured URL:
{ "database": "default", "collection": "notifications", "id": "record-id-123", "action": "create", "timestamp": "2024-01-01T00:00:00Z", "record": { "id": "record-id-123", "title": "New Notification", "message": "Hello World", "status": "active", "created": "2024-01-01T00:00:00Z", "updated": "2024-01-01T00:00:00Z" }}Practical Examples
Section titled “Practical Examples”Slack Notifications
Section titled “Slack Notifications”collections: alerts: type: base fields: - {name: severity, type: select, options: {values: [low, medium, high, critical]}} - {name: message, type: text, required: true} - {name: source, type: text} options: triggers: - action: create url: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK method: POST headers: Content-Type: application/jsonThe Slack webhook would receive:
{ "text": "New Alert: Critical - Server down"}Email Notifications
Section titled “Email Notifications”collections: contact_submissions: type: base fields: - {name: name, type: text, required: true} - {name: email, type: email, required: true} - {name: message, type: text, required: true} options: triggers: - action: create url: https://email-service.com/send method: POST headers: Authorization: "Bearer ${EMAIL_API_KEY}" Content-Type: application/jsonAudit Logging
Section titled “Audit Logging”collections: users: type: auth fields: - {name: email, type: email, required: true} - {name: password, type: text, required: true} - {name: name, type: text} options: triggers: - action: create url: https://audit-log.example.com/entry method: POST headers: Authorization: "Bearer ${AUDIT_TOKEN}" - action: update url: https://audit-log.example.com/entry method: POST headers: Authorization: "Bearer ${AUDIT_TOKEN}" - action: delete url: https://audit-log.example.com/entry method: POST headers: Authorization: "Bearer ${AUDIT_TOKEN}"Secrets like
EMAIL_API_KEYshould not be committed in plain YAML headers. Prefer pipeline secrets or an environment-driven templating step in CI when the values are sensitive.
Third-Party Integration
Section titled “Third-Party Integration”collections: leads: # CRM integration type: base fields: - {name: name, type: text, required: true} - {name: email, type: email, required: true} - {name: company, type: text} options: triggers: - action: create url: https://crm.example.com/api/leads method: POST headers: Authorization: "Bearer ${CRM_API_KEY}" Content-Type: application/json
events: # analytics integration type: base fields: - {name: eventName, type: text, required: true} - {name: userId, type: text} - {name: properties, type: json} options: triggers: - action: create url: https://analytics.example.com/track method: POST headers: Authorization: "Bearer ${ANALYTICS_KEY}"Workflow Triggers
Section titled “Workflow Triggers”collections: orders: type: base fields: - {name: customerId, type: text, required: true} - {name: items, type: json, required: true} - {name: total, type: number, required: true} - {name: status, type: select, options: {values: [pending, processing, shipped, delivered]}} options: triggers: - action: create url: http://127.0.0.1:3000/api/workflows/runs method: POST headers: Authorization: "Bearer ${WORKFLOW_API_KEY}" Content-Type: application/jsonPipeline Triggers
Section titled “Pipeline Triggers”Pipelines can subscribe to collection events directly in their own definition, so no collection trigger or auth header wiring is needed:
{ "name": "orders-pipeline", "triggers": [ { "type": "collection", "database": "default", "collection": "orders", "actions": ["create"] } ]}The record (and its id, action, database and collection) reaches every step
as {{trigger.record.*}} templates.
Data Synchronization
Section titled “Data Synchronization”collections: products: type: base fields: - {name: sku, type: text, required: true} - {name: name, type: text, required: true} - {name: price, type: number, required: true} - {name: stock, type: number} options: triggers: - action: create url: https://external-db.example.com/sync method: POST headers: Authorization: "Bearer ${SYNC_KEY}" - action: update url: https://external-db.example.com/sync method: PATCH headers: Authorization: "Bearer ${SYNC_KEY}" - action: delete url: https://external-db.example.com/sync method: DELETE headers: Authorization: "Bearer ${SYNC_KEY}"Trigger Configuration
Section titled “Trigger Configuration”Configuration Options
Section titled “Configuration Options”# action is one of: create | update | delete# method is one of: GET | POST | PUT | PATCH | DELETE (optional, default POST)- action: create url: https://api.example.com/hook method: POST headers: {Content-Type: application/json} # optional custom headers body: "custom body" # optional custom bodyMultiple Triggers
Section titled “Multiple Triggers”You can define multiple triggers for different events, and multiple triggers for the same event; each fires independently:
options: triggers: - {action: create, url: "https://api.example.com/new", method: POST} - {action: create, url: "https://slack.example.com/webhook", method: POST} - {action: create, url: "https://email.example.com/send", method: POST} - {action: update, url: "https://api.example.com/updated", method: PATCH} - {action: delete, url: "https://api.example.com/deleted", method: DELETE}Updating Collection Triggers
Section titled “Updating Collection Triggers”Edit the YAML and apply. The options block is authoritative: whatever the
file says is what the collection carries after vski apply.
Webhook Logs
Section titled “Webhook Logs”View webhook execution logs from the command line or the SDK:
vski webhooks logs --collection notificationsvski webhooks logs --status failedlogs = await client.webhook_logs.list( collection="notifications", status="success", page=1, per_page=50)for log in logs: print(log["url"], log["status"], log["tries"], log.get("error_msg"))Trigger Execution Flow
Section titled “Trigger Execution Flow”Event occurs in collection ↓Trigger is matched ↓HTTP request is queued ↓Request is sent to target URL ↓Response is received ↓Log entry is created ↓(If failed) Retry mechanism kicks inError Handling
Section titled “Error Handling”Failed Triggers
Section titled “Failed Triggers”When a trigger fails (non-2xx response):
- Error is logged
- Retry counter is incremented
- Trigger will retry on next attempt
Viewing Failed Triggers
Section titled “Viewing Failed Triggers”vski webhooks logs --status failedfor log in await client.webhook_logs.list(status="failed"): print("failed:", log["url"], log.get("error_msg"), "tries:", log["tries"])Security Best Practices
Section titled “Security Best Practices”- Use HTTPS: always use HTTPS URLs for triggers
- Secure tokens: store API tokens securely (environment variables)
- Verify signatures: implement webhook signature verification on the receiving end
- Rate limiting: implement rate limiting on webhook endpoints
- Validate payloads: always validate incoming webhook data
- Use authentication: include authentication headers in triggers
- Monitor logs: regularly check webhook logs for failures
- Test endpoints: verify webhook endpoints are working before production
- Handle duplicates: make webhook handlers idempotent
- Timeout handling: set appropriate timeouts for webhook requests
Trigger vs Cron Jobs
Section titled “Trigger vs Cron Jobs”| Feature | Triggers | Cron Jobs |
|---|---|---|
| When | On record events | On schedule |
| Use Cases | Real-time notifications, data sync | Periodic tasks, reports |
| Latency | Near real-time | Based on schedule |
| Reliability | Depends on endpoint | Built-in retry |
| API | Collection options | client.cron |
Use triggers for:
- Immediate notifications
- Real-time data synchronization
- Event-driven workflows
- Third-party integrations
Use cron jobs for:
- Scheduled tasks
- Periodic reports
- Maintenance operations
- Batch processing
Testing Triggers
Section titled “Testing Triggers”Manual Trigger Test
Section titled “Manual Trigger Test”Create a test record to trigger the webhook, then check the logs:
await client.collection("notifications").create( {"title": "Test Notification", "message": "This is a test", "status": "active"})logs = await client.webhook_logs.list(collection="notifications", page=1, per_page=10)print(logs)Using Testing Tools
Section titled “Using Testing Tools”Use webhook testing services like:
- https://webhook.site - Get a temporary webhook URL
- https://requestbin.com - Inspect HTTP requests
- ngrok - Expose local endpoints
collections: test_collection: type: base fields: - {name: data, type: text, required: true} options: triggers: - action: create url: https://webhook.site/your-unique-id method: POSTawait client.collection("test_collection").create({"data": "Test data"})# then check webhook.site to see the payloadAPI Endpoints
Section titled “API Endpoints”| Method | Endpoint | Description |
|---|---|---|
POST | /api/collections | Create collection with triggers |
PATCH | /api/collections/:name | Update collection triggers |
GET | /api/webhooks/logs | List webhook execution logs |
Best Practices
Section titled “Best Practices”- Keep endpoints fast: webhook endpoints should respond quickly
- Return success codes: always return 2xx status codes on success
- Handle failures gracefully: don’t throw unhandled errors
- Log everything: keep detailed logs for debugging
- Monitor regularly: check webhook logs for issues
- Use idempotency: handle duplicate webhook deliveries
- Implement timeouts: don’t let webhook handlers hang
- Validate input: always validate webhook payloads
- Test thoroughly: test triggers before production
- Document triggers: the YAML is the documentation