Docs/Product guides

Find the framework route, component prop, SDK method, or HTTP contract you need.

Approvals: policy to enforcement

An approval request captures the proposed action, snapshots the matching policy, notifies reviewers, and records their decisions. Your server checks the result immediately before performing the protected operation.

Create and check a request

const request = await incld.approvals.create(
 {
  resourceType: "release",
  resourceId: "rel_42",
  action: "publish",
  title: "Publish August release",
  metadata: { environment: "production" },
  expiresAt: "2026-08-25T00:00:00Z",
 },
 { idempotencyKey: "release:rel_42:publish:v1" },
)

const check = await incld.approvals.check({
 resourceType: "release",
 resourceId: "rel_42",
 action: "publish",
})

Here incld is a server client constructed with the authenticated organization and user scope. The API supplies those trusted identities rather than accepting them from the request payload.

The check returns approved, the current status or none, and an optional approval ID.

Policies and matching

await incld.approvalPolicies.create({
 resourcePattern: "release:*",
 allowedApprovers: ["user_ops_1", "user_ops_2", "user_security"],
 mode: "quorum",
 requiredApprovals: 2,
 allowSelfApproval: false,
})
ModeResolution rule
any The first valid approval resolves the request.
all Every allowed approver must approve.
quorum requiredApprovals valid approvals are required, capped by the allowed reviewer count.

An organization-specific policy wins over a project-global policy with the same matching specificity. Its ID, revision, reviewer set, mode, threshold, and self-approval setting are copied into the request. Later policy edits affect only new requests. Policy organization scope is immutable. A project-global fallback can only be defined with a deliberately unscoped administrator client; tenant-scoped clients cannot mutate it.

Choose policy scope deliberately

The project dashboard is an administrator surface, so its customer-scope selector can inspect one external organization or all data scopes. New dashboard policies default to a customer organization and require its external ID. Select Project fallback explicitly only when the policy should be eligible across every organization without a more-specific override.

Decisions and lifecycle commands

await incld.approvals.approve(
 approvalId,
 "Risk checks complete",
 { idempotencyKey: `approval:${approvalId}:user_ops_1` },
)

await incld.approvals.reject(approvalId, "Missing evidence")
await incld.approvals.cancel(approvalId, "No longer needed")
await incld.approvals.revoke(approvalId, "Conditions changed")
StateHow it is reached
pending Request created and waiting for policy resolution.
approved The policy threshold is satisfied.
rejected A valid reviewer rejects the request.
cancelled The requester/application cancels pending work.
expired expiresAt passes while the request remains pending.
revoked A previously approved request is invalidated.

Reviewer notifications

Configure Slack Incoming Webhooks and verified Amazon SES email destinations from the Approvals area of the dashboard. New requests and lifecycle changes enqueue delivery; failed notification attempts are retried independently from approval state.

Notification sinks are project-wide

Dashboard-configured Slack and email destinations are operator sinks for the whole incld project, so they can receive events from every organization. Do not use them as end-customer notification channels. Send tenant-specific notifications from your own application after receiving a scoped webhook event.
DestinationSetup
Slack Paste an Incoming Webhook URL. @incld probes it before marking the destination connected.
Email Add an address, deliver the signed verification link, and verify before notifications are sent.
Sender Production email requires INCLD_EMAIL_FROM and configured Amazon SES credentials/region.

Enforce the approval in application code

export async function publishRelease(releaseId: string, session: Session) {
 const tenantIncld = new Incld({
  apiKey: process.env.INCLD_SECRET_KEY!,
  scope: {
   organizationId: session.organization.id,
   userId: session.user.id,
  },
 })
 const state = await tenantIncld.approvals.check({
  resourceType: "release",
  resourceId: releaseId,
  action: "publish",
 })
 if (!state.approved) throw new Error("Approval required")
 return releases.publish(releaseId)
}