Skip to main content
Waitpoint tokens pause a task run indefinitely until you signal it to continue. While a run is waiting, it releases its compute slot — you are not billed for idle time. When you complete the token, the run resumes exactly where it left off. Common use cases:
  • Human approval — pause a content pipeline until a moderator approves
  • External webhook callback — pause until Replicate, Stripe, or another service posts a result
  • Multi-step forms — pause an onboarding flow until a user completes the next step
  • Async third-party jobs — kick off a long job and wait for the completion webhook
If you are waiting for typed data from an input stream, use inputStream.wait() instead — it wraps waitpoint tokens with full type safety.

Basic usage

1

Create a token

Create a token from anywhere in your codebase — inside or outside a task:
import { wait } from "@trigger.dev/sdk";

const token = await wait.createToken({
  timeout: "24h", // optional: auto-complete with a timeout error after 24 hours
  tags: ["moderation", "user:123"], // optional: visible in the dashboard
});

// token.id  — pass this to wait.forToken()
// token.url — POST to this URL to complete the token from any HTTP client
2

Wait for the token inside a task

Inside your task’s run function, call wait.forToken() with the token ID. The task suspends here until the token is completed or times out:
import { task, wait } from "@trigger.dev/sdk";

type ApprovalResult = {
  approved: boolean;
  comment?: string;
};

export const moderationTask = task({
  id: "moderation",
  run: async (payload: { content: string }) => {
    const token = await wait.createToken({ timeout: "24h" });

    // Notify your moderation team — send token.id or token.url
    await sendToModerationQueue({ content: payload.content, tokenId: token.id });

    // Task suspends here. Compute slot is released.
    const result = await wait.forToken<ApprovalResult>(token.id);

    if (!result.ok) {
      // Token timed out
      return { status: "timed_out" };
    }

    return {
      status: result.output.approved ? "approved" : "rejected",
      comment: result.output.comment,
    };
  },
});

async function sendToModerationQueue(opts: { content: string; tokenId: string }) {
  // send email, Slack message, etc.
}
3

Complete the token

When you are ready to resume the run, complete the token from your backend, a webhook handler, or any HTTP client:
import { wait } from "@trigger.dev/sdk";

await wait.completeToken<ApprovalResult>(tokenId, {
  approved: true,
  comment: "Looks good!",
});

Using .unwrap()

wait.forToken() returns a result object ({ ok, output } or { ok, error }). Use .unwrap() to skip the if (result.ok) check — it throws automatically if the token timed out:
// Without unwrap:
const result = await wait.forToken<ApprovalResult>(token.id);
if (!result.ok) throw result.error;
const approval = result.output;

// With unwrap (equivalent, but cleaner happy path):
const approval = await wait.forToken<ApprovalResult>(token.id).unwrap();
console.log(approval.approved); // boolean

Completing via HTTP webhook

The token’s url property is an HTTP callback URL. POST a JSON body to it to complete the token from any service or language. The request body becomes the token’s output:
const token = await wait.createToken({ timeout: "10m" });

// Pass token.url as a webhook URL to an external service
const prediction = await replicate.predictions.create({
  version: "27b93a2413e7f36cd83da926f3656280b2931564ff050bf9575f1fdf9bcd7478",
  input: { prompt: "A painting of a cat by Andy Warhol" },
  webhook: token.url,
  webhook_events_filter: ["completed"],
});

// The task resumes when Replicate POSTs to token.url
const result = await wait.forToken<typeof prediction>(token.id).unwrap();
You can also complete tokens via the REST API from any language:
curl -X POST "https://api.trigger.dev/api/v1/waitpoints/tokens/{tokenId}/complete" \
  -H "Authorization: Bearer {your-secret-key}" \
  -H "Content-Type: application/json" \
  -d '{"data": {"approved": true, "comment": "LGTM"}}'

API reference

wait.createToken

Create a waitpoint token. Can be called from inside or outside a task.
const token = await wait.createToken({
  timeout: "10m",          // optional, defaults to "10m"
  idempotencyKey: "key",   // optional
  idempotencyKeyTTL: "1h", // optional, defaults to "1h"
  tags: ["tag1", "tag2"],  // optional
});
Returns:
PropertyTypeDescription
idstringToken ID (starts with waitpoint_)
urlstringHTTP callback URL — POST to this to complete the token
isCachedbooleantrue if an idempotency key matched an existing token
publicAccessTokenstringA Public Access Token scoped to this token

wait.forToken

Suspend the current task run until the token is completed or times out. Must be called inside a task’s run function.
const result = await wait.forToken<YourOutputType>(tokenId);

if (result.ok) {
  console.log(result.output); // YourOutputType
} else {
  console.log(result.error);  // WaitpointTimeoutError
}

// Or use .unwrap() to throw on timeout:
const output = await wait.forToken<YourOutputType>(tokenId).unwrap();

wait.completeToken

Complete a token with an output payload. Can be called from anywhere.
await wait.completeToken<YourOutputType>(tokenId, {
  /* your payload */
});

wait.retrieveToken

Fetch the current state and output of a token by its ID.
const token = await wait.retrieveToken(tokenId);
// token.status: "WAITING" | "COMPLETED" | "TIMED_OUT"
// token.output — the data passed to completeToken()
// token.error  — set if status is "TIMED_OUT"

wait.listTokens

List tokens with optional filtering. Returns an async iterable — iterate with for await:
import { wait } from "@trigger.dev/sdk";

for await (const token of wait.listTokens({
  status: "WAITING",
  tags: ["moderation"],
  period: "1h",
})) {
  console.log(token.id, token.status);
}

Idempotency

Pass an idempotencyKey to createToken to avoid creating duplicate tokens if your code runs more than once (e.g. on a task retry):
const token = await wait.createToken({
  idempotencyKey: `approve-${documentId}`,
  idempotencyKeyTTL: "24h",
  timeout: "24h",
});
// If the same key is used again within the TTL, the original token is returned.
// token.isCached === true in that case

Next steps

Building with AI

Human-in-the-loop patterns for AI agent workflows.

React hooks

Resume tokens from the browser using the useWaitToken hook.

Realtime overview

Stream task output to your frontend in real-time.

Build docs developers (and LLMs) love