Connect sync and domain services to app entitlements
Use this contract when a separately deployed sync or domain service must enforce the same app rights as Workspace. The service can introspect online, maintain a local projection, or validate a short-lived signed grant. Raw payment, identity, and provider evidence remains inside Workspace.
Choose an integration mode
| Mode | Best for | Contract |
|---|---|---|
| Native resolver | Code in the same Workspace process | Direct tenant-isolated domain call |
| Introspection | One online decision | Current state for one app actor |
| Snapshot and change feed | A local service projection | Full initial state followed by ordered changes |
| Ed25519 grant | Short offline decision | Audience-bound, short-lived signed evidence |
| Webhook | Low-latency notification | At-least-once delivery; the feed remains the reconciliation contract |
For a standalone sync service, use snapshot and change feed as the default. Add webhooks for faster notification, not as a replacement for reconciliation.
Authenticate the service
An administrator creates a tenant- and app-bound service binding with an audience and the required capabilities, then rotates its credential. The secret is returned only when issued.
Send these headers with each authenticated request:
X-Workspace-Service-CredentialX-Workspace-Service-TimestampX-Workspace-Service-NonceX-Workspace-Service-Signature
The signature covers the HTTP method, full request URI including its query, timestamp, nonce, and SHA-256 body hash. Generate a fresh cryptographic nonce for every attempt. Stale timestamps, invalid signatures, and nonce reuse fail closed.
Use the endpoints
| Method and path | Purpose |
|---|---|
GET /api/v1/public/v1/app-entitlements/service/introspect/{actorId} | Read one actor's current entitlement |
GET /api/v1/public/v1/app-entitlements/service/snapshot | Load the paged initial state |
GET /api/v1/public/v1/app-entitlements/service/changes?afterSequence=... | Continue with ordered changes |
POST /api/v1/public/v1/app-entitlements/service/grants/{actorId} | Issue a short-lived grant |
GET /api/v1/public/v1/app-entitlements/jwks | Load public Ed25519 verification keys |
Persist asOfSequence from the first snapshot page. Send both cursor and the same asOfSequence on every following page, then consume changes after that sequence in ascending order. If the feed returns ERR_APP_ENTITLEMENT_CURSOR_EXPIRED, discard the local projection and start with a new snapshot.
Validate grants and webhooks
Cache the JWKS and validate signature, key ID, issuer, audience, and expiry. Never accept an expired grant or use it for another service binding.
Process webhooks durably before acknowledging them. Deduplicate by event ID and sequence. Delivery is at least once and may be delayed. Reconcile any gap through the change feed. Workspace permits only secured HTTPS targets and blocks private and loopback destinations; never place credentials in the target URL.
Verify the integration
Test valid introspection, snapshot/feed continuation, the wrong audience, an expired grant, stale timestamps, nonce replay, duplicate webhook delivery, and cursor expiry followed by a fresh snapshot. Do not derive rights directly from a provider notification or store payload. Workspace remains the entitlement authority.