Documentation Index
Fetch the complete documentation index at: https://docs.velahq.xyz/docs/llms.txt
Use this file to discover all available pages before exploring further.
Data model
Account
└── App (one account, many apps)
├── API Key — ingest events into this app
├── Schema (many) — defines the shape of each event type
├── Event (many) — validated payloads stored by Vela
├── Notification Rule (many) — conditional alert triggers
└── Destination (many) — Slack / Discord / Email / Webhook
Everything in Vela is scoped to an App. An app is the boundary between your services — one app per microservice, per environment, or per product area. You decide.
Accounts
An account represents your organization. It can own multiple apps and has a plan (Free, Pro, Team) that controls event limits and app quotas. You authenticate with one or more client secrets for management operations.
Apps
An app is an isolated workspace. Everything — schemas, events, rules — belongs to an app.
| Property | Description |
|---|
| name | Human-readable label, e.g. Order Service |
| slug | URL-safe unique ID, e.g. order-service |
| API key | vela_live_... — used only for event ingestion |
Common patterns:
- One app per microservice (
payments, auth, fulfillment)
- One app per environment (
order-service-prod, order-service-staging)
- One app per product (
web-app, mobile-app)
Schemas
A schema defines the expected shape of an event payload. Every event sent to Vela is validated against the schema for its event name. Events without a matching schema are rejected with 400.
Why schemas matter
Without schemas, bad data silently enters your system. A schema enforces:
- Required fields — the event is rejected if they are missing
- Field types —
string, number, boolean, date, enum, object
- Validation rules — min/max length, regex patterns, enum values
- Metadata fields — optional contextual data like environment or trace ID
Example schema
{
"eventName": "payment.failed",
"description": "Fired when a payment attempt fails",
"fields": [
{ "id": "f1", "name": "orderId", "type": "string", "required": true },
{ "id": "f2", "name": "amountCents", "type": "number", "required": true },
{ "id": "f3", "name": "reason", "type": "enum", "required": true,
"enumValues": ["card_declined", "insufficient_funds", "expired_card"] },
{ "id": "f4", "name": "attemptNum", "type": "number", "required": false }
],
"metadataFields": [
{ "id": "m1", "name": "environment", "type": "string" },
{ "id": "m2", "name": "traceId", "type": "string" }
]
}
Field types
| Type | Description | Validation |
|---|
string | Text | min, max (length), pattern (regex) |
number | Integer or float | min, max |
boolean | true or false | — |
date | ISO-8601 date string | — |
enum | One of a fixed set | enumValues array required |
object | Nested JSON | — |
Use the CLI to manage schemas as JSON files in your repository. Schema changes become PR-reviewable, just like database migrations.
Events
Events are the core data unit. An event represents something that happened in your system.
Event fields
| Field | Required | Description |
|---|
event | yes | Must match a registered schema’s eventName |
data | yes | Payload validated against schema fields |
level | yes | info, warning, error, or success |
customer_id | no | Optional customer identifier for filtering |
metadata | no | Optional context validated against metadataFields |
timestamp | no | ISO-8601 — defaults to server ingest time |
Event levels
| Level | When to use | Examples |
|---|
info | Normal business events | order.placed, user.signed_up |
success | Completed flows | payment.captured, email.delivered |
warning | Degraded, non-critical | retry.attempt, rate_limit_approaching |
error | Failures needing attention | payment.failed, webhook.error |
Event lifecycle
POST /v1/ingest
↓
Schema validation — reject 400 if invalid
↓
Saved to PostgreSQL
↓
Published to message bus (events.ingested)
↓
┌─────────────────┬──────────────────────┐
▼ ▼ ▼
Rules engine Realtime WebSocket Future consumers
evaluates pushes event to
conditions connected dashboards
↓
Matching rules publish notifications.deliver
↓
Delivery sends to Slack / Discord / Email / Webhook
↓
Attempt logged: status, response code, error
↓
On failure: retry up to 3x (30s delay) → DLQ
Notification Rules
A rule watches for events of a specific type and fires when conditions match. Evaluation is real-time — rules trigger as soon as an event is ingested.
Rule fields
| Field | Description |
|---|
name | Human-readable label |
eventName | Which event type to watch |
conditions | Optional payload filters — all must pass |
actions | One or more delivery targets |
enabled | Toggle on/off without deleting |
Conditions
A rule with no conditions fires for every matching event. Conditions filter by payload fields:
{
"conditions": [
{ "field": "amountCents", "operator": "gt", "value": 10000 },
{ "field": "currency", "operator": "eq", "value": "USD" },
{ "field": "reason", "operator": "in", "value": ["card_declined", "fraud"] },
{ "field": "customerEmail", "operator": "contains", "value": "@enterprise.com" }
]
}
| Operator | Description |
|---|
eq / neq | Equals / not equals |
gt / gte | Greater than / greater than or equal |
lt / lte | Less than / less than or equal |
contains | String contains substring |
in / not_in | Value is in / not in array |
Actions
Each action points to a destination. A single rule can have multiple actions — alert Slack and email at the same time.
{
"actions": [
{ "id": "a1", "destinationId": "dest-uuid", "channel": "slack", "enabled": true },
{ "id": "a2", "destinationId": "dest-uuid", "channel": "email", "enabled": true },
{ "id": "a3", "destinationId": "dest-uuid", "channel": "discord", "enabled": false }
]
}
Destinations
Destinations are delivery targets configured in the dashboard. Create them once, then reference by ID in rules.
| Type | Description |
|---|
slack | Slack incoming webhook — formatted block message |
discord | Discord webhook — color-coded embed |
email | Email via Resend API |
webhook | Custom HTTP endpoint — POSTs full event payload as JSON |
Destination credentials are encrypted at rest with AES-256-GCM.
Authentication
| Credential | Format | Purpose | Header |
|---|
| Client secret | vela_cs_... | Management API | Authorization: Bearer |
| API key | vela_live_... | Event ingestion only | x-api-key |
The separation is intentional. Your API key travels with every ingest call — if it leaks, an attacker can only send events, not read or modify your data.
See the credentials guide.