Developers · PickCounter API v1

Embed pickup verification into your product.

Operators with a PickCounter account can generate API keys and call our REST endpoints from their own backend — useful when you operate a delivery service, a POS platform, or your own ordering surface and want pickup verification baked in.

Base URL: https://pickcounter.ioFormat: application/jsonAuth: Bearer pck_…

Authentication

All requests must include an Authorization header with a Bearer token. Generate keys in Settings → API keys. Keys are shown only once at creation; if you lose one, revoke it and create a new one.

Header
Authorization: Bearer pck_eXaMpLe_DO_not_use_this_one

Keys are scoped to a single organization and a single set of permissions (default: verifications:write). Each request touches last_used_at on the key.

POST/api/v1/verifications

Create a verification

Creates an order, sends a single SMS to the assigned driver with a unique check-in link, and returns a verification record. Idempotent on (source, external_order_id, location_id) — re-sending the same identifier returns 409 Conflict.

Request body

FieldTypeRequiredDescription
external_order_idstringYesYour order identifier. Unique per location.
customer_namestringYesEnd-customer name. Truncated to first + last initial on the counter display.
driver_phonestringYesE.164 format (e.g. +15035551234).
driver_namestringNoDisplay name. Defaults to the existing driver record if known.
location_iduuidNoDefaults to the org's first location if omitted.

Example — curl

curl -X POST https://pickcounter.io/api/v1/verifications \
  -H "Authorization: Bearer pck_..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_order_id": "4421",
    "customer_name": "Thomas Johnson",
    "driver_phone": "+15035550101",
    "driver_name": "Sarah M."
  }'

Example — Node

const res = await fetch("https://pickcounter.io/api/v1/verifications", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.HANDOFF_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    external_order_id: "4421",
    customer_name: "Thomas Johnson",
    driver_phone: "+15035550101",
    driver_name: "Sarah M.",
  }),
});
const verification = await res.json();

Response — 201 Created

{
  "object": "verification",
  "id": "f3a1c2b8-0c10-4b3a-9d3a-58a4c0b8e7c2",
  "order_id": "5b9d2c00-9e1c-44d2-9f2e-77ac0c9e1d11",
  "status": "notified",
  "sms_sent": true,
  "sms_sid": "SM5b9d2c..."
}
GET/api/v1/verifications/{id}

Retrieve a verification

Returns current status and timestamps for a verification.

Response — 200 OK

{
  "object": "verification",
  "id": "f3a1c2b8-0c10-4b3a-9d3a-58a4c0b8e7c2",
  "order_id": "5b9d2c00-9e1c-44d2-9f2e-77ac0c9e1d11",
  "external_order_id": "4421",
  "customer_name": "Thomas Johnson",
  "driver_name": "Sarah M.",
  "status": "checked_in",
  "notified_at": "2026-04-19T22:41:08Z",
  "checked_in_at": "2026-04-19T22:48:55Z",
  "handed_off_at": null,
  "checkin_method": "qr",
  "failure_reason": null
}

Errors

Errors return JSON with a single error field and a standard HTTP status. Common cases:

StatusMeaning
400Bad request — body failed validation.
401Missing or invalid Bearer token.
403Key valid but lacks the required scope.
409Order with that external_order_id already exists.
429Rate limited. Retry after a short backoff.
503Service temporarily unavailable.

Rate limits

Each API key is limited to 120 requests per minute on the verifications endpoint. Limits exist primarily to keep SMS spend predictable; if you have a legitimate higher-volume use case, email hello@pickcounter.io.

Webhooks (coming soon)

Subscribe to verification lifecycle events (order.notified, order.checked_in, order.handed_off). HMAC-signed POST callbacks. We'll publish the schema once the first integration partner is live.