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 the platform. The service can introspect online, maintain a local projection, or validate a short-lived signed grant. Raw payment, identity, and provider evidence remains on the server.

Choose an integration mode

ModeBest forContract
Native resolverCode in the same Workspace processDirect tenant-isolated domain call
IntrospectionOne online decisionCurrent state for one app actor
Snapshot and change feedA local service projectionFull initial state followed by ordered changes
Ed25519 grantShort offline decisionAudience-bound, short-lived signed evidence
WebhookLow-latency notificationAt-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-Nucleus-App-Service-Credential
  • X-Nucleus-App-Service-Timestamp
  • X-Nucleus-App-Service-Nonce
  • X-Nucleus-App-Service-Signature

The signature covers the HTTP method, full request URI including its query, timestamp, nonce, and the SHA-256 hash of the body bytes actually sent. A signed request body must not exceed 128 KiB. Generate a fresh cryptographic nonce for every attempt. Stale timestamps, invalid signatures, and nonce reuse fail closed.

For the remainder of the current major, the complete legacy set of X-Workspace-Service-Credential, X-Workspace-Service-Timestamp, X-Workspace-Service-Nonce, and X-Workspace-Service-Signature remains compatible. Send either the complete canonical set or the complete legacy set. Mixed and incomplete sets are rejected. New integrations use only the canonical headers.

Use the endpoints

Method and pathPurpose
GET /api/v1/public/v1/app-entitlements/service/introspect/{actorId}Read one actor's current entitlement
GET /api/v1/public/v1/app-entitlements/service/snapshotLoad 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/jwksLoad 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 the signature, key ID, issuer, audience, nbf, and exp. The exp claim already uses the earlier boundary of the service binding's grant TTL and the effective entitlement's known validity. Treat valid_until as informational and never as a substitute for validating exp.

A valid token alone does not grant access. Also validate the state and the specific features or limits required by the operation. 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. The server permits only secured HTTPS targets and blocks private and loopback destinations; never place credentials in the target URL. During the compatibility window, webhooks contain both X-Nucleus-App-Service-Key and X-Workspace-Service-Key with the same value. New receivers use the canonical header and reject delivery if both values are present but differ.

Verify the integration

Test valid introspection, snapshot/feed continuation, the wrong audience, an expired grant, an entitlement that ends before the grant TTL, 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. The server remains the entitlement authority.