Skip to main content

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.

File naming

Each schema lives in its own .json file, named after the event:
vela/schemas/
  order.placed.json
  order.cancelled.json
  payment.failed.json
  user.signed-up.json
The filename is just for humans — the eventName field inside the file is what Vela uses to match incoming events. By convention, keep them in sync.

File structure

{
  "eventName": "order.placed",
  "description": "Emitted when a customer completes checkout",
  "fields": [
    {
      "id": "f1",
      "name": "orderId",
      "type": "string",
      "required": true,
      "description": "Opaque order identifier",
      "validation": { "min": 1, "max": 128 }
    },
    {
      "id": "f2",
      "name": "amountCents",
      "type": "number",
      "required": true,
      "description": "Total order amount in cents",
      "validation": { "min": 0 }
    },
    {
      "id": "f3",
      "name": "currency",
      "type": "enum",
      "required": true,
      "enumValues": ["USD", "EUR", "GBP", "CAD"]
    },
    {
      "id": "f4",
      "name": "customerEmail",
      "type": "string",
      "required": false,
      "validation": { "pattern": "^[^@]+@[^@]+\\.[^@]+$" }
    },
    {
      "id": "f5",
      "name": "itemCount",
      "type": "number",
      "required": false,
      "defaultValue": 1
    }
  ],
  "metadataFields": [
    {
      "id": "m1",
      "name": "environment",
      "type": "string",
      "description": "e.g. production, staging"
    },
    {
      "id": "m2",
      "name": "traceId",
      "type": "string",
      "description": "Distributed trace identifier"
    }
  ]
}

Top-level fields

FieldRequiredDescription
eventNameyesThe event name that must match when ingesting. Dot-separated by convention: order.placed, payment.failed.
descriptionnoHuman-readable description of what this event represents. Shown in the dashboard.
fieldsyesArray of field definitions. Minimum one field.
metadataFieldsnoOptional contextual fields (environment, trace ID, version). Not part of the core payload.

Field properties

PropertyRequiredDescription
idyesUnique identifier for this field within the schema. Use a stable value — changing an id doesn’t affect matching, but it’s confusing.
nameyesThe key name in the event payload (e.g. orderId). Case-sensitive.
typeyesData type — see the table below.
requiredyestrue → event is rejected if this field is missing. false → field is optional.
descriptionnoHuman-readable description. Shown in the dashboard.
defaultValuenoDefault value used when the field is absent and required is false.
enumValuesconditionalRequired when type is "enum". Array of allowed string values.
validationnoAdditional validation constraints (see below).

Field types

TypeDescriptionValidation options
stringText valuemin (min length), max (max length), pattern (regex)
numberInteger or floatmin, max
booleantrue or false
dateISO-8601 date string (e.g. 2024-06-01T12:00:00Z)
enumOne value from a fixed set of stringsenumValues array required
objectNested JSON object

Validation examples

{ "type": "string", "validation": { "min": 1, "max": 64 } }
{ "type": "string", "validation": { "pattern": "^ord_[a-z0-9]+$" } }
{ "type": "number", "validation": { "min": 0, "max": 1000000 } }
{ "type": "enum",   "enumValues": ["card_declined", "insufficient_funds", "expired_card"] }

Metadata fields

Metadata fields hold optional contextual data that isn’t part of the core event payload — things like deployment environment, trace IDs, or client version. They have the same id, name, type, and description properties as regular fields, but:
  • They are always optional (no required property)
  • They have no defaultValue or validation
{
  "metadataFields": [
    { "id": "m1", "name": "environment", "type": "string" },
    { "id": "m2", "name": "traceId",     "type": "string" },
    { "id": "m3", "name": "version",     "type": "string" }
  ]
}
When ingesting, pass metadata in the metadata key:
await ingest.ingest({
  event: 'order.placed',
  data: { orderId: 'ord_1', amountCents: 4999, currency: 'USD' },
  level: 'info',
  metadata: { environment: 'production', traceId: 'abc-123' },
});

How diffing works

When you run vela diff or vela push, schemas are matched by eventName. For each schema:
  • No remote match → schema will be created
  • Remote exists, no changesskipped
  • Remote exists, changes detected → schema will be updated
Fields are compared by name, not id. You can change a field’s id without triggering an update — only changes to name, type, required, enumValues, or validation count as changes.
When updating, the entire fields array replaces the remote version. Removing a field from the local file removes it from the schema. Make sure downstream consumers are updated before removing required fields.