Docs/Start here

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

Next.js quickstart

Ship a working customer-facing schedule in a Next.js App Router project. Connect the trusted browser proxy and signed webhook, register one action, and render the scheduling UI; Approvals, Bulk, and Audit reuse the same integration.

Before you start

You needWhere it comes fromWhy
Project secret key Dashboard → project API keys Authenticates server API calls.
Webhook signing secret Dashboard → project delivery settings Verifies that action deliveries came from @incld.
Authenticated application user Your existing server session Scopes browser requests and supplies trusted actor identity.
Schedules entitlement Developer project or paid Schedules plan Allows schedule creation and execution.

This guide assumes Next.js App Router and an existing auth() helper. Substitute your own session library; the invariant is that identity comes from the server, never request JSON.

1. Install the packages

npm install @incld/client @incld/react @incld/react-schedules\nnpm install --save-dev tsx

Import the shared and feature styles once in your application stylesheet:

@import "@incld/react/styles.css";
@import "@incld/react-schedules/styles.css";

2. Configure server secrets

.env.local bash
INCLD_SECRET_KEY=sk_live_replace_me
INCLD_WEBHOOK_SECRET=whsec_replace_me
INCLD_API_URL=https://api.incld.dev

Never prefix these variables with NEXT_PUBLIC_

The secret key authenticates your project and the webhook secret verifies delivery. Neither value belongs in a browser bundle.

3. Create the server integration

src/lib/incld.ts ts
import "server-only"
import { createIncld, defineActions } from "@incld/client/next"
import { auth } from "@/lib/auth"
import { reports } from "@/lib/reports"

const actions = defineActions({
 generate_report: {
  displayName: "Generate report",
  payloadSchema: {
   type: "object",
   properties: { accountId: { type: "string" } },
   required: ["accountId"],
  },
  async run({ payload, event }) {
   await reports.generate(payload.accountId, {
    idempotencyKey: event.idempotencyKey,
   })
  },
 },
})

export const incld = createIncld({
 apiKey: process.env.INCLD_SECRET_KEY!,
 webhookSecret: process.env.INCLD_WEBHOOK_SECRET!,
 baseUrl: process.env.INCLD_API_URL,
 actions,
 async resolveContext() {
  const session = await auth()
  if (!session?.user.id || !session.organizationId) return null
  return {
   user: { id: session.user.id },
   organization: { id: session.organizationId },
   roles: session.user.roles,
   permissions: session.user.permissions,
  }
 },
 async authorize({ context, operation }) {
  return context.permissions?.includes(`incld:${operation}`) === true
 },
})

4. Mount separate proxy and webhook routes

app/api/incld/v1/[...path]/route.ts ts
import { incld } from "@/lib/incld"

export const dynamic = "force-dynamic"
export const { GET, POST, PATCH, DELETE } = incld.routes
app/api/incld/webhook/route.ts ts
import { incld } from "@/lib/incld"

export const dynamic = "force-dynamic"
export const POST = incld.webhook

The provider base URL is /api/incld; the browser client appends /v1, which is why the catch-all route lives below /api/incld/v1. The browser route authenticates your user. The webhook route authenticates @incld. Do not combine them.

5. Add the shared React provider

app/providers.tsx tsx
"use client"

import { IncldProvider, type IncldProviderProps } from "@incld/react"

export function Providers({ children }: { children: IncldProviderProps["children"] }) {
 return (
  <IncldProvider
   baseUrl="/api/incld"
   appearance={{ colorScheme: "system", accentColor: "indigo" }}
   onError={(error) => console.error(error.code, error.requestId)}
  >
   {children}
  </IncldProvider>
 )
}

6. Render a component

"use client"

import { ScheduleList, ScheduleTrigger } from "@incld/react-schedules"

export function ReportAutomation() {
 return (
  <section>
   <ScheduleTrigger
    action="generate_report"
    defaultPayload={{ accountId: "acct_42" }}
   >
    Schedule report
   </ScheduleTrigger>
   <ScheduleList filters={{ action: "generate_report" }} />
  </section>
 )
}

7. Sync actions

scripts/sync-incld.ts ts
import { incld } from "../src/lib/incld"

await incld.syncActions()
console.log("incld actions synchronized")
package.json json
{
 "scripts": {
  "incld:sync": "tsx scripts/sync-incld.ts"
 }
}
npm run incld:sync

Run this after deploying code and before users create Schedules or Bulk operations. Synchronization is an idempotent upsert, so repeating it during every deploy is safe.

8. Verify the request and delivery paths

  1. 01

    Confirm the action registry

    The dashboard should show generate_report after the sync command completes. A missing action means the server key, API URL, or sync process is wrong.

  2. 02

    Configure delivery

    Set the dashboard webhook endpoint to https://your-app.example/api/incld/webhook and copy the matching signing secret.

  3. 03

    Exercise the browser proxy

    While signed in to your app, create a schedule. The browser should call /api/incld/v1/schedules without any Authorization header or identity field supplied by your component.

  4. 04

    Observe delivery

    Trigger or wait for a run. Confirm a 2xx webhook response, one handler execution for the event idempotency key, and the resulting run status in the dashboard.

If the first request fails

ResultMost likely cause
401 context_required resolveContext returned null or did not resolve a trusted user.
403 organization_context_required A tenant-bound request had no trusted organization.id. Resolve it from the server session.
403 context_forbidden authorize rejected the exact operation, such as schedules.read or schedules.create.
403 component_not_enabled Schedules is not enabled for this project.
422 action_not_declared The delivered action was not included in defineActions in the running deployment.
401 signature_invalid Wrong webhook secret, modified request body, or more than five minutes of clock skew.
404 route_not_found Provider baseUrl and the /api/incld/v1 catch-all route do not align.