Skip to content

Extensions

Extension mechanism for optional capabilities


Extensions add optional capabilities to Vend without modifying the core protocol. They enable experimentation and domain-specific features while maintaining interoperability.


  1. Explicit — Extensions MUST be declared in the request
  2. Contained — Extension data MUST be within the extension object
  3. Additive — Extensions MUST only add, not modify core behavior
  4. Negotiated — Both client and server MUST support the extension

Extensions are declared in the extensions array as objects:

{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_123",
"call": {
"function": "reports.generate",
"version": "1",
"arguments": { "type": "quarterly" }
},
"extensions": [
{
"urn": "urn:vnd:ext:async",
"options": {
"preferred": true,
"callback_url": "https://my-service.example.com/webhooks"
}
}
]
}
FieldTypeRequiredDescription
urnstringYesExtension identifier URN
optionsobjectNoExtension-specific request options

Responses mirror the request structure:

{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_123",
"result": { "queued": true },
"extensions": [
{
"urn": "urn:vnd:ext:tracing",
"data": {
"trace_id": "tr_abc123",
"span_id": "span_server_001",
"duration": { "value": 45, "unit": "millisecond" }
}
},
{
"urn": "urn:vnd:ext:async",
"data": {
"operation_id": "op_xyz789",
"status": "processing",
"poll": {
"function": "vend.operation.status",
"version": "1",
"arguments": { "operation_id": "op_xyz789" }
}
}
}
]
}
FieldTypeRequiredDescription
urnstringYesExtension identifier URN (echoed from request)
dataobjectNoExtension-specific response data

Each extension is identified by a URN per RFC 8141 (Uniform Resource Names):

urn:vnd:ext:async
urn:vnd:ext:example:audit

Rules:

  • MUST be a valid URN per RFC 8141
  • SHOULD resolve to documentation
  • SHOULD use HTTPS

When a server supports all declared extensions:

  1. Process extension-specific options
  2. Return response with same extensions (by URN)
  3. Include extension-specific response data

When a server does not support a declared extension:

{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_123",
"result": null,
"errors": [{
"code": "EXTENSION_NOT_SUPPORTED",
"message": "Extension not supported: urn:vnd:ext:example:unknown",
"retryable": false,
"details": {
"unsupported": ["urn:vnd:ext:example:unknown"],
"supported": ["urn:vnd:ext:async"]
}
}]
}

Servers MUST ignore extension objects with unrecognized URIs if the request does not require them.

While vend.capabilities returns server-wide extension support, individual functions MAY restrict which extensions they accept.

When a client requests an extension that a function doesn’t support:

{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_123",
"result": null,
"errors": [{
"code": "EXTENSION_NOT_APPLICABLE",
"message": "Caching extension not applicable to write operations",
"retryable": false,
"source": { "pointer": "/extensions/0" },
"details": {
"extension": "urn:vnd:ext:caching",
"function": "orders.create"
}
}]
}

Use vend.describe to discover which extensions a specific function supports. See System Functions for details.


An extension specification MUST define:

  1. URN — Unique identifier
  2. Options — Request options the extension accepts
  3. Data — Response data the extension returns
  4. Behavior — Processing rules
  5. Examples — Usage examples
# Audit Extension
**URN:** urn:vnd:ext:audit
## Options (Request)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `actor.user_id` | string | Yes | User performing the action |
| `actor.ip_address` | string | No | Client IP address |
| `actor.reason` | string | No | Reason for action |
## Data (Response)
| Field | Type | Description |
|-------|------|-------------|
| `log_id` | string | Audit log entry identifier |
| `logged_at` | string | ISO 8601 timestamp |
## Behavior
When the audit extension is included:
1. Server MUST log the action with actor details
2. Server MUST return `log_id` referencing the log entry
3. Log entries MUST be immutable once created

ExtensionURNDescription
Asyncurn:vnd:ext:asyncLong-running operations with polling/callbacks
Batchurn:vnd:ext:batchMultiple operations in a single request
Cachingurn:vnd:ext:cachingETags, cache hints, conditional requests
Deadlineurn:vnd:ext:deadlineRequest timeouts to prevent cascading failures
Deprecationurn:vnd:ext:deprecationWarnings for deprecated functions/versions
Dry Runurn:vnd:ext:dry-runValidate operations without executing
Idempotencyurn:vnd:ext:idempotencyRequest deduplication for safe retries
Localeurn:vnd:ext:localeInternationalization and localization preferences
Maintenanceurn:vnd:ext:maintenanceScheduled downtime and graceful degradation
Priorityurn:vnd:ext:priorityRequest priority hints for queue management
Queryurn:vnd:ext:queryFiltering, sorting, pagination, relationships
Quotaurn:vnd:ext:quotaUsage quotas and limits information
Rate Limiturn:vnd:ext:rate-limitRequest throttling and usage visibility
Redacturn:vnd:ext:redactSensitive field masking and data protection
Replayurn:vnd:ext:replayRequest journaling for deferred execution
Tracingurn:vnd:ext:tracingDistributed tracing for observability

Clients MAY discover supported extensions:

// Request
{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_discover",
"call": {
"function": "vend.capabilities",
"version": "1",
"arguments": {}
}
}
// Response
{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_discover",
"result": {
"protocol_versions": ["0.1.0"],
"extensions": [
{
"urn": "urn:vnd:ext:async",
"documentation": "urn:vnd:ext:async"
}
]
}
}

See System Functions for details.


{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_ext",
"call": {
"function": "users.delete",
"version": "1",
"arguments": { "user_id": 42 }
},
"context": {
"trace_id": "tr_abc",
"span_id": "sp_123"
},
"extensions": [
{
"urn": "urn:vnd:ext:audit",
"options": {
"actor": {
"user_id": "admin_1",
"ip_address": "192.168.1.1",
"reason": "GDPR deletion request"
}
}
}
]
}
{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_ext",
"result": {
"deleted": true
},
"extensions": [
{
"urn": "urn:vnd:ext:tracing",
"data": {
"trace_id": "tr_abc123",
"span_id": "span_server_001",
"duration": { "value": 45, "unit": "millisecond" }
}
},
{
"urn": "urn:vnd:ext:audit",
"data": {
"log_id": "audit_log_789",
"logged_at": "2024-03-15T14:30:00Z"
}
}
]
}
{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_multi",
"call": {
"function": "reports.generate",
"version": "1",
"arguments": { "type": "quarterly" }
},
"extensions": [
{
"urn": "urn:vnd:ext:audit",
"options": {
"actor": { "user_id": "admin_1" }
}
},
{
"urn": "urn:vnd:ext:async",
"options": {
"preferred": true
}
}
]
}
{
"protocol": { "name": "vend", "version": "0.1.0" },
"id": "req_multi",
"result": null,
"extensions": [
{
"urn": "urn:vnd:ext:tracing",
"data": {
"trace_id": "tr_abc123",
"span_id": "span_server_001",
"duration": { "value": 12, "unit": "millisecond" }
}
},
{
"urn": "urn:vnd:ext:audit",
"data": {
"log_id": "audit_log_790"
}
},
{
"urn": "urn:vnd:ext:async",
"data": {
"operation_id": "op_abc123",
"status": "processing",
"poll": {
"function": "vend.operation.status",
"version": "1",
"arguments": { "operation_id": "op_abc123" }
},
"retry_after": { "value": 5, "unit": "second" }
}
}
]
}