Connector
Connector protocol schemas
Connector Protocol - LEVEL 3: Enterprise Connector
Defines the standard connector specification for external system integration. Connectors enable ObjectStack to sync data with SaaS apps, databases, file storage, and message queues through a unified protocol.
Positioning in the sync/integration layering — this file is now the ONLY
layer. Both layers above it were retired under ADR-0049 for the same measured
reason, that no engine ever executed them: L1 "Simple Sync"
(automation/sync.zod.ts) in #4738, and L2 "ETL Pipeline"
(automation/etl.zod.ts) in #6414. See
packages/spec/docs/SYNC_ARCHITECTURE.md:
- Enterprise Connector (THIS FILE) - System integrators - Full SAP integration; connector-attached sync via
syncConfig
SCOPE: Most comprehensive integration layer. Includes authentication, webhooks, field mapping, bidirectional sync, retry policies, and complete lifecycle management.
This protocol supports multiple authentication strategies, bidirectional sync, field mapping, webhooks, and comprehensive retry and resilience policies.
What this layer does NOT provide
There is no outbound rate limiting. This header used to advertise "rate
limiting" twice — once in the SCOPE line, once as "comprehensive rate limiting" —
and no engine ever backed either. connector.rateLimitConfig, and the entire
ConnectorRateLimitConfig / RateLimitStrategy shape behind it, was removed in
@objectstack/spec 17.0.0 (#4911, ADR-0049 D2), because no outbound
rate-limiting engine ever existed. The platform's only token bucket (runtime
security/rate-limit.ts) throttles INBOUND requests to us; nothing throttles
the calls a connector makes out. Do not substitute shared's
RateLimitConfig — that is the inbound limiter and would cap the wrong direction.
Until an outbound throttle exists, rate-limit at the connector provider or
upstream gateway. What L3 does declare for a rate-limited upstream is
retryConfig — whose retryableStatusCodes default [408, 429, 500, 502, 503, 504] includes 429 — and health.circuitBreaker. The full removal reasoning is
recorded at the removal site: the "REMOVED: outbound rate limiting" block in
integration/connector.zod.ts, and packages/spec/docs/SYNC_ARCHITECTURE.md.
Field mapping does not transform values. This header used to offer "field
mapping and transformations"; only the first half was ever true.
ConnectorFieldMappingSchema extends the base mapping with exactly three keys —
dataType, required and syncMode. FieldMapping.transform was removed in
@objectstack/spec 17.0.0 (#5552, ADR-0049), and the whole FieldMappingTransform
union went with it (constant / cast / lookup / javascript / map) — no
runtime ever executed any of the five. An L3 connector mapping moves a value from
source to target; it does not compute one. Value conversion belongs on a
surface that runs it: the import mapping's own mapping.fieldMapping[].transform
(data/mapping.zod.ts — a string enum,
none/constant/map/split/join/lookup, with its settings in params),
applied row by row by the REST import path — or an ETL transformation step
(L2 above). Already authored the retired key? os migrate meta --from 16 rewrites
existing sources automatically — the key itself is removed.
Runtime contract — descriptor vs. registered connector (#2612)
This schema serves TWO distinct consumers; do not conflate them:
- Runtime registration (plugin-only). The automation engine's connector
registry — what
GET /connectorslists and theconnector_actionflow node dispatches — is populated exclusively by plugins callingengine.registerConnector(def, handlers)with a handler per declared action (ADR-0018 §Addendum). The definition is validated against this schema at registration. - Declarative
connectors:stack entries (catalog descriptors). Stack metadata validated against this schema is registered as kind 'connector' for discovery/documentation/marketplace purposes only — it never reaches the runtime registry, because an action here carries no execution binding (deliberately: ADR-0023 rejected re-inventing OpenAPI inside this schema). The automation service warns at boot about declared entries withactionsthat lack a same-name runtime registration; mark deliberate catalog-only entries withenabled: false. Provider-bound declarative instances that a generic executor (connector-openapi / connector-mcp) materializes at boot are tracked in #2977 (ADR-0097).
Authentication is now imported from the canonical auth/config.zod.ts.
When to Use This Layer
Use Enterprise Connector when:
- Building enterprise-grade connectors (e.g., Salesforce, SAP, Oracle)
- Complex OAuth2/SAML authentication required
- Bidirectional sync with field mapping (
dataType/syncModeper field — it moves values, it does not transform them) - Webhook management required
- Full CRUD operations and data synchronization
- Need comprehensive retry strategies and error handling
Examples:
- Full Salesforce integration with webhooks
- SAP ERP connector with CDC (Change Data Capture)
- Microsoft Dynamics 365 connector
When to downgrade:
- Per-field value conversion on import only → the import mapping's own
transform(data/mapping.zod.ts), which the REST import path executes row by row. (This used to point atautomation/etl.zod.ts; L2 was retired at #6414 for having no executor, so the pointer would have been a signpost landing nowhere — the same defect class this header names below.)
There is no "Trigger Registry" alternative
This header used to carry a "When to use Integration Connector vs. Trigger
Registry?" comparison, steering "lightweight" cases to
automation/trigger-registry.zod.ts. That file was a third declaration of
the connector vocabulary with zero consumers — nothing registered, validated
or executed against it — so the guidance pointed authors, with the
platform's authority, at a dead end (#4499; removed alongside the #4480
per-provider template cluster). The same defect class as the
capabilities.readOnly prescription #4487 corrected: a signpost must land
somewhere enforced. Lightweight cases are served HERE — a connector instance
with simple auth. (Both automation-side layers were themselves retired as
dead ends of the same class: L1 "Simple Sync" in #4738, L2 etl.zod.ts in
#6414. This paragraph named L2 as the transformation destination until the
second retirement; a signpost that must land somewhere enforced cannot make
an exception for itself.)
Source: packages/spec/src/integration/connector.zod.ts
TypeScript Usage
import { CircuitBreakerConfigSchema, ConnectorSchema, ConnectorActionSchema, ConnectorActionEffectSchema, ConnectorConflictResolutionSchema, ConnectorErrorCategorySchema, ConnectorFieldMappingSchema, ConnectorHealthSchema, ConnectorInstanceAPIKeyAuthSchema, ConnectorInstanceAuthSchema, ConnectorInstanceBasicAuthSchema, ConnectorInstanceBearerAuthSchema, ConnectorInstanceNoAuthSchema, ConnectorRetryStrategySchema, ConnectorStatusSchema, ConnectorTriggerSchema, ConnectorTypeSchema, DataSyncConfigSchema, DeclarativeConnectorEntrySchema, ErrorMappingConfigSchema, ErrorMappingRuleSchema, HealthCheckConfigSchema, RetryConfigSchema, SyncStrategySchema, WebhookConfigSchema, WebhookEventSchema, WebhookSignatureAlgorithmSchema } from '@objectstack/spec/integration';
import type { CircuitBreakerConfig, Connector, ConnectorAction, ConnectorActionEffect, ConnectorConflictResolution, ConnectorErrorCategory, ConnectorFieldMapping, ConnectorHealth, ConnectorInstanceAPIKeyAuth, ConnectorInstanceAuth, ConnectorInstanceBasicAuth, ConnectorInstanceBearerAuth, ConnectorInstanceNoAuth, ConnectorRetryStrategy, ConnectorStatus, ConnectorTrigger, ConnectorType, DataSyncConfig, DeclarativeConnectorEntry, ErrorMappingConfig, ErrorMappingRule, HealthCheckConfig, RetryConfig, SyncStrategy, WebhookConfig, WebhookEvent, WebhookSignatureAlgorithm } from '@objectstack/spec/integration';
// Validate data
const result = CircuitBreakerConfigSchema.parse(data);CircuitBreakerConfig
Circuit breaker configuration
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| enabled | boolean | ✅ | Enable circuit breaker |
| failureThreshold | number | ✅ | Failures before opening circuit |
| resetTimeoutMs | number | ✅ | Time in open state before half-open |
| halfOpenMaxRequests | number | ✅ | Requests allowed in half-open state |
| monitoringWindow | number | ✅ | Rolling window for failure count in ms |
| fallbackStrategy | Enum<'cache' | 'default_value' | 'error' | 'queue'> | optional | Fallback strategy when circuit is open |
Connector
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Unique connector identifier |
| label | string | ✅ | Display label |
| type | Enum<'saas' | 'database' | 'file_storage' | 'message_queue' | 'api' | 'custom'> | ✅ | Connector type |
| description | string | optional | Connector description |
| icon | string | optional | Icon identifier |
| authentication | { type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } | { type: 'api-key'; key: string; headerName?: string; paramName?: string } | { type: 'basic'; username: string; password: string } | { type: 'bearer'; token: string } | { type: 'none' } | optional | Authentication configuration (runtime shape with inline secrets — plugin-supplied at registerConnector). Authored entries must not inline secrets (#7990): use auth.credentialRef on a provider-bound instance. |
| provider | string | optional | Generic-executor key that materializes this declarative entry at boot (e.g. openapi/mcp/rest). Omit for a catalog-only descriptor. Unknown provider ⇒ hard boot error (ADR-0097). |
| providerConfig | Record<string, any> | optional | Provider-specific config validated by the provider factory at boot (e.g. { spec, baseUrl } for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires provider. |
| auth | { type: 'none' } | { type: 'bearer'; credentialRef: string } | { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } | { type: 'basic'; username: string; credentialRef: string } | optional | Declarative instance auth — references credentials via credentialRef (resolved at boot), never inline secrets. Requires provider (ADR-0097). |
| actions | { key: string; label: string; description?: string; inputSchema?: Record<string, any>; … }[] | optional | |
| triggers | { key: string; label: string; description?: string; type: Enum<'polling' | 'webhook'>; … }[] | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) |
| syncConfig | { strategy?: Enum<'full' | 'incremental' | 'upsert' | 'append_only'>; direction?: Enum<'import' | 'export' | 'bidirectional'>; schedule?: string | object; realtimeSync?: boolean; … } | optional | Data sync configuration |
| fieldMappings | { source: string; target: string; defaultValue?: any; dataType?: Enum<'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'array'>; … }[] | optional | Field mapping rules |
| webhooks | { name: string; label?: string; object?: string; triggers?: Enum<'create' | 'update' | 'delete' | 'bulk_update' | 'bulk_delete'>[]; … }[] | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) |
| rateLimitConfig | never | optional | [REMOVED] connector.rateLimitConfig was removed in @objectstack/spec 17.0.0 (#4911, ADR-0049 D2) — the entire shape is gone, not just this key: ConnectorRateLimitConfig and its RateLimitStrategy enum were removed with it, because no outbound rate-limiting engine ever existed. The platform's only token bucket (runtime security/rate-limit.ts) throttles INBOUND requests to us; nothing throttled the calls a connector makes out, so every knob here was inert while reading like a configured cap. Delete the key. Do NOT substitute shared RateLimitConfig — that is the inbound limiter and would cap the wrong direction; until an outbound throttle exists, rate-limit at the connector provider or upstream gateway. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| retryConfig | { strategy?: Enum<'exponential_backoff' | 'linear_backoff' | 'fixed_delay' | 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … } | optional | Retry configuration |
| connectionTimeoutMs | number | optional | Connection timeout in ms |
| requestTimeoutMs | number | optional | Request timeout in ms |
| status | Enum<'active' | 'inactive' | 'error' | 'configuring'> | optional | Connector status |
| enabled | boolean | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). |
| errorMapping | { rules: object[]; defaultCategory?: Enum<'validation' | 'authorization' | 'not_found' | 'conflict' | 'rate_limit' | … +3 more>; unmappedBehavior: Enum<'passthrough' | 'generic_error' | 'throw'>; logUnmapped?: boolean } | optional | Error mapping configuration |
| health | { healthCheck?: object; circuitBreaker?: object } | optional | Health and resilience configuration |
| metadata | Record<string, any> | optional | Custom connector metadata |
| _lock | Enum<'none' | 'no-overlay' | 'no-delete' | 'full'> | optional | Item-level lock — controls overlay & delete (ADR-0010). |
| _lockReason | string | optional | Human-readable reason shown when a write is refused by _lock. |
| _lockSource | Enum<'artifact' | 'package' | 'env-forced'> | optional | Layer that set _lock (artifact | package | env-forced). |
| _provenance | Enum<'package' | 'org' | 'env-forced'> | optional | Origin of the item (package | org | env-forced). |
| _packageId | string | optional | Owning package machine id. |
| _packageVersion | string | optional | Owning package version. |
| _lockDocsUrl | string | optional | Optional documentation link surfaced next to _lockReason. |
ConnectorAction
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| key | string | ✅ | Action key (machine name) |
| label | string | ✅ | Human readable label |
| description | string | optional | |
| inputSchema | Record<string, any> | optional | Input parameters schema (JSON Schema) |
| outputSchema | Record<string, any> | optional | Output schema (JSON Schema) |
| effect | Enum<'read' | 'write'> | optional | What the action does upstream: 'read' never mutates (reports acted:0); 'write' does (a successful dispatch reports acted:1). Omit when the effect is not knowable — the step is then reported as unmeasured, not as zero |
ConnectorActionEffect
What the action does upstream: 'read' never mutates (reports acted:0); 'write' does (a successful dispatch reports acted:1). Omit when the effect is not knowable — the step is then reported as unmeasured, not as zero
Allowed Values
readwrite
ConnectorConflictResolution
Conflict resolution strategy
Allowed Values
source_winstarget_winslatest_winsmanual
ConnectorErrorCategory
Standard error category
Allowed Values
validationauthorizationnot_foundconflictrate_limittimeoutserver_errorintegration_error
ConnectorFieldMapping
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| source | string | ✅ | Source field name |
| target | string | ✅ | Target field name |
| transform | never | optional | [REMOVED] FieldMapping.transform — authored as connector.fieldMappings[].transform and externalLookup.fieldMappings[].transform — was removed in @objectstack/spec 17.0.0 (#5552, ADR-0049), and the whole FieldMappingTransform union went with it (constant / cast / lookup / javascript / map) — no runtime ever executed any of the five, and the javascript member advertised dialect: "js", a dialect retired in #3278. Delete the key. The transform pipeline that IS enforced is the import mapping's: mapping.fieldMapping[].transform (a string enum — none/constant/map/split/join/lookup — with its settings in params), applied by the REST import path, which rejects javascript with a 400 rather than pretending to run it. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| defaultValue | any | optional | Default if source is null/undefined |
| dataType | Enum<'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'array'> | optional | Target data type |
| required | boolean | ✅ | Field is required |
| syncMode | Enum<'read_only' | 'write_only' | 'bidirectional'> | ✅ | Sync mode |
ConnectorHealth
Connector health configuration
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| healthCheck | { enabled: boolean; intervalMs: number; timeoutMs: number; endpoint?: string; … } | optional | Health check configuration |
| circuitBreaker | { enabled: boolean; failureThreshold: number; resetTimeoutMs: number; halfOpenMaxRequests: number; … } | optional | Circuit breaker configuration |
ConnectorInstanceAPIKeyAuth
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'api-key' | ✅ | |
| credentialRef | string | ✅ | Secrets-layer reference resolved to the API key at materialization. Never an inline key. |
| headerName | string | optional | HTTP header carrying the key (default X-API-Key). |
| paramName | string | optional | Query parameter carrying the key (alternative to header). |
ConnectorInstanceAuth
Union Options
This schema accepts one of the following structures:
Option 1
Type: none
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'none' | ✅ |
Option 2
Type: bearer
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'bearer' | ✅ | |
| credentialRef | string | ✅ | Secrets-layer reference (e.g. an env-var name in the open tier) resolved to the bearer token at materialization. Never an inline token. |
Option 3
Type: api-key
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'api-key' | ✅ | |
| credentialRef | string | ✅ | Secrets-layer reference resolved to the API key at materialization. Never an inline key. |
| headerName | string | optional | HTTP header carrying the key (default X-API-Key). |
| paramName | string | optional | Query parameter carrying the key (alternative to header). |
Option 4
Type: basic
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'basic' | ✅ | |
| username | string | ✅ | Username (not a secret; safe to keep in metadata). |
| credentialRef | string | ✅ | Secrets-layer reference resolved to the password at materialization. Never an inline password. |
ConnectorInstanceBasicAuth
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'basic' | ✅ | |
| username | string | ✅ | Username (not a secret; safe to keep in metadata). |
| credentialRef | string | ✅ | Secrets-layer reference resolved to the password at materialization. Never an inline password. |
ConnectorInstanceBearerAuth
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'bearer' | ✅ | |
| credentialRef | string | ✅ | Secrets-layer reference (e.g. an env-var name in the open tier) resolved to the bearer token at materialization. Never an inline token. |
ConnectorInstanceNoAuth
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'none' | ✅ |
ConnectorRetryStrategy
Retry strategy
Allowed Values
exponential_backofflinear_backofffixed_delayno_retry
ConnectorStatus
Connector status
Allowed Values
activeinactiveerrorconfiguring
ConnectorTrigger
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| key | string | ✅ | Trigger key |
| label | string | ✅ | Trigger label |
| description | string | optional | |
| type | Enum<'polling' | 'webhook'> | ✅ | Trigger type |
| interval | number | optional | Polling interval in seconds |
ConnectorType
Connector type
Allowed Values
saasdatabasefile_storagemessage_queueapicustom
DataSyncConfig
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| strategy | Enum<'full' | 'incremental' | 'upsert' | 'append_only'> | optional | Synchronization strategy |
| direction | Enum<'import' | 'export' | 'bidirectional'> | optional | Sync direction |
| schedule | string | { dialect: Enum<'cel' | 'cron' | 'template'>; source?: string; ast?: any; meta?: object } | optional | Cron expression for scheduled sync — cron0 */15 * * * |
| realtimeSync | boolean | optional | Enable real-time sync |
| timestampField | string | optional | Field to track last modification time |
| conflictResolution | Enum<'source_wins' | 'target_wins' | 'latest_wins' | 'manual'> | optional | Conflict resolution strategy |
| batchSize | number | optional | Records per batch |
| deleteMode | Enum<'hard_delete' | 'soft_delete' | 'ignore'> | optional | Delete handling mode |
| filters | Record<string, any> | optional | Filter criteria for selective sync |
DeclarativeConnectorEntry
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Unique connector identifier |
| label | string | ✅ | Display label |
| type | Enum<'saas' | 'database' | 'file_storage' | 'message_queue' | 'api' | 'custom'> | ✅ | Connector type |
| description | string | optional | Connector description |
| icon | string | optional | Icon identifier |
| authentication | { type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } | { type: 'api-key'; key: string; headerName?: string; paramName?: string } | { type: 'basic'; username: string; password: string } | { type: 'bearer'; token: string } | { type: 'none' } | optional | Authentication configuration (runtime shape with inline secrets — plugin-supplied at registerConnector). Authored entries must not inline secrets (#7990): use auth.credentialRef on a provider-bound instance. |
| provider | string | optional | Generic-executor key that materializes this declarative entry at boot (e.g. openapi/mcp/rest). Omit for a catalog-only descriptor. Unknown provider ⇒ hard boot error (ADR-0097). |
| providerConfig | Record<string, any> | optional | Provider-specific config validated by the provider factory at boot (e.g. { spec, baseUrl } for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires provider. |
| auth | { type: 'none' } | { type: 'bearer'; credentialRef: string } | { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } | { type: 'basic'; username: string; credentialRef: string } | optional | Declarative instance auth — references credentials via credentialRef (resolved at boot), never inline secrets. Requires provider (ADR-0097). |
| actions | { key: string; label: string; description?: string; inputSchema?: Record<string, any>; … }[] | optional | |
| triggers | { key: string; label: string; description?: string; type: Enum<'polling' | 'webhook'>; … }[] | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) |
| syncConfig | { strategy?: Enum<'full' | 'incremental' | 'upsert' | 'append_only'>; direction?: Enum<'import' | 'export' | 'bidirectional'>; schedule?: string | object; realtimeSync?: boolean; … } | optional | Data sync configuration |
| fieldMappings | { source: string; target: string; defaultValue?: any; dataType?: Enum<'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'array'>; … }[] | optional | Field mapping rules |
| webhooks | { name: string; label?: string; object?: string; triggers?: Enum<'create' | 'update' | 'delete' | 'bulk_update' | 'bulk_delete'>[]; … }[] | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) |
| rateLimitConfig | never | optional | [REMOVED] connector.rateLimitConfig was removed in @objectstack/spec 17.0.0 (#4911, ADR-0049 D2) — the entire shape is gone, not just this key: ConnectorRateLimitConfig and its RateLimitStrategy enum were removed with it, because no outbound rate-limiting engine ever existed. The platform's only token bucket (runtime security/rate-limit.ts) throttles INBOUND requests to us; nothing throttled the calls a connector makes out, so every knob here was inert while reading like a configured cap. Delete the key. Do NOT substitute shared RateLimitConfig — that is the inbound limiter and would cap the wrong direction; until an outbound throttle exists, rate-limit at the connector provider or upstream gateway. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| retryConfig | { strategy?: Enum<'exponential_backoff' | 'linear_backoff' | 'fixed_delay' | 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … } | optional | Retry configuration |
| connectionTimeoutMs | number | optional | Connection timeout in ms |
| requestTimeoutMs | number | optional | Request timeout in ms |
| status | Enum<'active' | 'inactive' | 'error' | 'configuring'> | optional | Connector status |
| enabled | boolean | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). |
| errorMapping | { rules: object[]; defaultCategory?: Enum<'validation' | 'authorization' | 'not_found' | 'conflict' | 'rate_limit' | … +3 more>; unmappedBehavior: Enum<'passthrough' | 'generic_error' | 'throw'>; logUnmapped?: boolean } | optional | Error mapping configuration |
| health | { healthCheck?: object; circuitBreaker?: object } | optional | Health and resilience configuration |
| metadata | Record<string, any> | optional | Custom connector metadata |
| _lock | Enum<'none' | 'no-overlay' | 'no-delete' | 'full'> | optional | Item-level lock — controls overlay & delete (ADR-0010). |
| _lockReason | string | optional | Human-readable reason shown when a write is refused by _lock. |
| _lockSource | Enum<'artifact' | 'package' | 'env-forced'> | optional | Layer that set _lock (artifact | package | env-forced). |
| _provenance | Enum<'package' | 'org' | 'env-forced'> | optional | Origin of the item (package | org | env-forced). |
| _packageId | string | optional | Owning package machine id. |
| _packageVersion | string | optional | Owning package version. |
| _lockDocsUrl | string | optional | Optional documentation link surfaced next to _lockReason. |
ErrorMappingConfig
Error mapping configuration
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| rules | { sourceCode: string | number; sourceMessage?: string; targetCode: string; targetCategory: Enum<'validation' | 'authorization' | 'not_found' | 'conflict' | 'rate_limit' | … +3 more>; … }[] | ✅ | Error mapping rules |
| defaultCategory | Enum<'validation' | 'authorization' | 'not_found' | 'conflict' | 'rate_limit' | 'timeout' | 'server_error' | 'integration_error'> | ✅ | Default category for unmapped errors |
| unmappedBehavior | Enum<'passthrough' | 'generic_error' | 'throw'> | ✅ | What to do with unmapped errors |
| logUnmapped | boolean | ✅ | Log unmapped errors |
ErrorMappingRule
Error mapping rule
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| sourceCode | string | number | ✅ | External system error code |
| sourceMessage | string | optional | Pattern to match against error message |
| targetCode | string | ✅ | ObjectStack standard error code |
| targetCategory | Enum<'validation' | 'authorization' | 'not_found' | 'conflict' | 'rate_limit' | 'timeout' | 'server_error' | 'integration_error'> | ✅ | Error category |
| severity | Enum<'low' | 'medium' | 'high' | 'critical'> | ✅ | Error severity level |
| retryable | boolean | ✅ | Whether the error is retryable |
| userMessage | string | optional | Human-readable message to show users |
HealthCheckConfig
Health check configuration
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| enabled | boolean | ✅ | Enable health checks |
| intervalMs | number | ✅ | Health check interval in milliseconds |
| timeoutMs | number | ✅ | Health check timeout in milliseconds |
| endpoint | string | optional | Health check endpoint path |
| method | Enum<'GET' | 'HEAD' | 'OPTIONS'> | optional | HTTP method for health check |
| expectedStatus | number | ✅ | Expected HTTP status code |
| unhealthyThreshold | number | ✅ | Consecutive failures before marking unhealthy |
| healthyThreshold | number | ✅ | Consecutive successes before marking healthy |
RetryConfig
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| strategy | Enum<'exponential_backoff' | 'linear_backoff' | 'fixed_delay' | 'no_retry'> | ✅ | Retry strategy |
| maxAttempts | number | ✅ | Maximum retry attempts |
| initialDelayMs | number | ✅ | Initial retry delay in ms |
| maxDelayMs | number | ✅ | Maximum retry delay in ms |
| backoffMultiplier | number | ✅ | Exponential backoff multiplier |
| retryableStatusCodes | number[] | ✅ | HTTP status codes to retry |
| retryOnNetworkError | boolean | ✅ | Retry on network errors |
| jitter | boolean | ✅ | Add jitter to retry delays |
SyncStrategy
Synchronization strategy
Allowed Values
fullincrementalupsertappend_only
WebhookConfig
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Webhook unique name (lowercase snake_case) |
| label | string | optional | Human-readable webhook label |
| object | string | optional | Object whose record events (create/update/delete, bulk_update/bulk_delete) trigger this webhook |
| triggers | Enum<'create' | 'update' | 'delete' | 'bulk_update' | 'bulk_delete'>[] | optional | Events that trigger execution |
| url | string | ✅ | External webhook endpoint URL |
| method | Enum<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'> | ✅ | HTTP method |
| headers | Record<string, string> | optional | Custom HTTP headers |
| timeoutMs | integer | ✅ | Request timeout in milliseconds |
| secret | string | optional | Signing secret for HMAC signature verification |
| isActive | boolean | ✅ | Whether webhook is active |
| description | string | optional | Webhook description |
| protection | { lock: Enum<'none' | 'no-overlay' | 'no-delete' | 'full'>; reason: string; docsUrl?: string } | optional | Package author protection block — lock policy for this webhook. |
| _lock | Enum<'none' | 'no-overlay' | 'no-delete' | 'full'> | optional | Item-level lock — controls overlay & delete (ADR-0010). |
| _lockReason | string | optional | Human-readable reason shown when a write is refused by _lock. |
| _lockSource | Enum<'artifact' | 'package' | 'env-forced'> | optional | Layer that set _lock (artifact | package | env-forced). |
| _provenance | Enum<'package' | 'org' | 'env-forced'> | optional | Origin of the item (package | org | env-forced). |
| _packageId | string | optional | Owning package machine id. |
| _packageVersion | string | optional | Owning package version. |
| _lockDocsUrl | string | optional | Optional documentation link surfaced next to _lockReason. |
| events | Enum<'record.created' | 'record.updated' | 'record.deleted' | 'sync.started' | 'sync.completed' | 'sync.failed' | 'auth.expired' | 'rate_limit.exceeded'>[] | optional | Connector events to subscribe to (not yet enforced — no runtime dispatches these; see #3197) |
| signatureAlgorithm | Enum<'hmac_sha256' | 'hmac_sha512' | 'none'> | ✅ | Webhook signature algorithm |
WebhookEvent
Webhook event type
Allowed Values
record.createdrecord.updatedrecord.deletedsync.startedsync.completedsync.failedauth.expiredrate_limit.exceeded
WebhookSignatureAlgorithm
Webhook signature algorithm
Allowed Values
hmac_sha256hmac_sha512none