Signups
A signup is one customer waiting on one variant over one channel. Internally these are Intent rows; v1 exposed them as "intents".
Path prefix: /back_in_stock/signups
The status field
Every signup carries a compiled lifecycle status, derived from its timestamps:
| Status | Meaning |
|---|---|
pending | Waiting — no notification sent yet |
notified | The back-in-stock alert was sent |
unsubscribed | The contact opted out |
blocked | A send was attempted but stopped (e.g. plan limits) |
Precedence when several timestamps are set: unsubscribed wins (the contact opted out), then notified, then blocked; a row with none of the three is pending.
The status filter on list accepts pending, notified, and unsubscribed only — blocked appears in response payloads but is not a filterable value. To find blocked sends, filter the notification log with status=blocked.
List signups
GET /back_in_stock/signups| Param | Type | Notes |
|---|---|---|
channel | email / sms / push | |
status | pending / notified / unsubscribed | |
variant_id | integer | Shopify variant id |
product_id | integer | Shopify product id |
email | string | Partial match on the signup's or its customer's email |
phone | string | Partial match on the signup's or its customer's phone |
from, to | ISO 8601 | Window on created_at |
page, per_page | integer | per_page default 50, max 200 |
Aliases: "waitlist", "show the waitlist", "notify me list", "who's waiting", "customers waiting for restock"
Response: { "signups": [...], "meta": { "total_count", "page", "per_page", "total_pages" } }.
Read a signup
GET /back_in_stock/signups/:id{
"id": "uuid",
"status": "pending",
"channel": "email",
"contact": "jane@example.com",
"quantity": 1,
"variant": { "shopify_variant_id": 123, "shopify_product_id": 456, "title": "Small / Blue" },
"customer": { "id": "uuid", "email": "jane@example.com", "name": "Jane" },
"optin": { "required": false, "confirmed": null },
"notifications_sent": 0,
"last_notified_at": null,
"created_at": "2026-06-01T12:00:00Z"
}optin.confirmed is only meaningful when the shop requires double opt-in (see Compliance settings); it is null otherwise.
Create a signup
POST /back_in_stock/signupsAdmin-side waitlist creation — the storefront widget keeps its own path. Finds or creates the customer, then creates the signup.
| Body field | Required | Notes |
|---|---|---|
channel | yes | email or sms |
shopify_variant_id | yes | |
email | for the email channel | |
phone | for the sms channel | |
shopify_product_id | no | Resolved from the variant when omitted |
quantity | no | integer, default 1 |
name | no | Customer name |
Returns: 201 Created with the full signup representation.
Notes: returns 409 Conflict when a pending signup already exists for the same contact + variant + channel — pending signups are unique per contact/variant/channel.
Delete a signup
DELETE /back_in_stock/signups/:idAliases: "remove from waitlist", "cancel restock alert", "take customer off the notify me list"
Notes: pending signups only — deleting an already-notified signup returns 409 Conflict (history is immutable).
Returns: { "deleted": true, "signup_id": "..." }.
Notify now
POST /back_in_stock/signups/:id/notifySends the back-in-stock notification for one signup immediately, regardless of stock state. Delivery runs through the normal notification pipeline.
| Body field | Type | Default | Notes |
|---|---|---|---|
allow_resend | boolean | false | Required to re-notify an already-notified signup |
Aliases: "send the restock email", "notify this customer", "resend restock notification"
Notes: returns 409 Conflict when the signup was already notified and allow_resend is not set.
Returns: 202 Accepted with { "signup_id": "...", "queued": true }.
Bulk notify
POST /back_in_stock/signups/bulk_notify| Body field | Required | Notes |
|---|---|---|
signup_ids | yes | Array of signup ids, max 1000 |
allow_resend | no | Same gate as single notify |
Aliases: "notify the waitlist", "notify everyone waiting", "send restock emails to the whole waitlist"
Responds 200 OK when every send queues, or 207 Multi-Status on partial failure — the body is the same shape either way, so always check failed:
{
"success": ["id-1", "id-2"],
"failed": [
{ "signup_id": "id-3", "error": "Signup was already notified. Pass allow_resend to send again." }
]
}Already-notified signups land in failed unless allow_resend is set. A 207 is not an error envelope — the successes in success really did queue.
Bulk delete
POST /back_in_stock/signups/bulk_delete| Body field | Required | Notes |
|---|---|---|
signup_ids | yes | Array of signup ids, max 1000 |
Aliases: "clear the waitlist", "purge pending restock signups"
Notes: pending-only, all-or-nothing — if any id in the batch was already notified, the whole request fails with 422 listing the offending ids, and nothing is deleted.
Returns: { "deleted_count": N }.
Transfer
POST /back_in_stock/signups/transferMoves signups from one variant to another — for variant merges or replacements.
| Body field | Required | Notes |
|---|---|---|
from_shopify_variant_id | yes | Must differ from the target |
to_shopify_variant_id | yes | |
to_shopify_product_id | yes | |
from_shopify_product_id | no | Resolved from the source variant when omitted |
scope | no | pending (default) / notified / all — which signups move |
Aliases: "move the waitlist", "merge waitlists", "shift restock signups to a new variant" Notes: signups that would duplicate a pending signup on the target variant are dropped rather than moved.
Returns: 202 Accepted with { "success": true, "scope": "pending", "from_shopify_variant_id": ..., "to_shopify_variant_id": ... }.
Notifications (the send log)
A separate, read-only resource: one entry per delivery attempt, including sends blocked by plan limits. Use it to answer "who actually got notified, and what didn't go out".
List notifications
GET /back_in_stock/notifications| Param | Type | Notes |
|---|---|---|
channel | email / sms / push | |
status | sent / blocked | |
variant_id | integer | Shopify variant id |
product_id | integer | Shopify product id |
from, to | ISO 8601 | |
page, per_page | integer | per_page default 50, max 200 |
Aliases: "back in stock send log", "who got notified", "notification history"
Notes: status=blocked surfaces sends stopped by plan limits (with blocked_reason).
Response: { "notifications": [...], "meta": {...} }.
Read a notification
GET /back_in_stock/notifications/:id{
"id": "uuid",
"status": "sent",
"blocked_reason": null,
"channel": "email",
"contact": "jane@example.com",
"shopify_variant_id": 123,
"shopify_product_id": 456,
"signup_id": "uuid",
"sent_at": "2026-06-01T12:00:00Z"
}signup_id links back to the signup the notification was sent for. For aggregate send counts over time, use the notifications report instead.
