Waitlist guides
04 · Approvals

Move entries through the funnel.

One at a time, or up to 100 per call. Approving can hand the person straight to an Auth app.


1

The status graph

An entry is pending, approved or rejected. New signups land approved unless the waitlist requires review, in which case they land pending.

  • pendingapproved | rejected
  • approvedrejected — you can always change your mind.

Anything else is 422 Invalid status transition. Setting an entry to the status it already has is an idempotent no-op: no event, no email, no audit row.


2

Approve or reject one entry

PATCH /v1/workspaces/:workspace_id/waitlists/:waitlist_id/entries/:entry_id
curl -X PATCH https://api.waitlist.productcraft.co/v1/workspaces/<workspace_id>/waitlists/<waitlist_id>/entries/<entry_id> \
  -H 'authorization: Bearer pcft_live_...' \
  -H 'content-type: application/json' \
  -d '{ "status": "approved" }'

status is the only field, and only approved / rejected are accepted — you cannot push an entry back to pending. The full updated entry comes back.

The transition emits entry.approved / entry.rejected to your webhooks (chapter 6) and, if the workspace has notifications enabled, sends the matching Mail template.


3

Bulk actions

One call, up to 100 entry ids, one action. Ids are de-duped server-side and processed serially, and you get a per-row result so partial failures are visible instead of silent.

POST /v1/workspaces/:workspace_id/waitlists/:waitlist_id/entries/bulk
curl https://api.waitlist.productcraft.co/v1/workspaces/<workspace_id>/waitlists/<waitlist_id>/entries/bulk \
  -H 'authorization: Bearer pcft_live_...' \
  -H 'content-type: application/json' \
  -d '{
    "ids":    ["150489b4-...", "72d214cd-..."],
    "action": "approve"
  }'
201 Created
{
  "requested": 2,
  "succeeded": 2,
  "failed":    0,
  "results": [
    { "id": "150489b4-...", "status": "ok" },
    { "id": "72d214cd-...", "status": "error",
      "error": { "code": "...", "message": "..." } }
  ]
}

action is approve, reject or invite-to-app. For invite-to-app you may also pass app_slug (overrides the workspace default) and send_email (default true); both are ignored for approve/reject.


4

Invite to an Auth app

The handoff: turn an approved entry into a user of your Auth consumer app, with an invite link emailed to them.

Approving does not invite automatically — invite is its own explicit action, so you can approve first and hand off later. Point the workspace at an app once:

PUT /v1/workspaces/:workspace_id/waitlist/settings
curl -X PUT https://api.waitlist.productcraft.co/v1/workspaces/<workspace_id>/waitlist/settings \
  -H 'authorization: Bearer pcft_live_...' \
  -H 'content-type: application/json' \
  -d '{ "default_heimdall_app_slug": "acme" }'

Then invite an entry:

POST .../entries/:entry_id/invite-to-app
curl -X POST https://api.waitlist.productcraft.co/v1/workspaces/<workspace_id>/waitlists/<waitlist_id>/entries/<entry_id>/invite-to-app \
  -H 'authorization: Bearer pcft_live_...' \
  -H 'content-type: application/json' \
  -d '{ "send_email": true }'

Body is optional: app_slug overrides the workspace default for this one call, send_email (default true) controls whether the approval email goes out with the invite link baked in. With neither an app_slug nor a workspace default you get 422 Workspace has no linked Auth app.

On success the entry records invited_at and invited_to_app_slug — read those back on GET .../entries/:entry_id to see who has already been handed off, and to make a re-run of the same batch safe.


5

Reviewing the queue

Entries only arrive through the public submit endpoint — there is no admin create. To work a queue:

  • GET .../entries?status=pending&limit=100 — cursor-paginated, returns { data, pagination: { next_cursor, has_more } }.
  • GET .../entries/count — total for the waitlist.
  • GET .../entries/export.csv — streams every entry; the right tool before a DELETE of the waitlist.
  • DELETE .../entries/:entry_id — removes one entry. Positions of the survivors are not renumbered.

6

Audit

Every status change writes a workspace audit row — rally.entry.approved / rally.entry.rejected, with the actor and the from/to statuses. Deletions record rally.entry.delete. Read them through the platform audit-log endpoint (see the Platform guides).