Skip to content

Ticketing partners

This guide is for external ticketing platforms that sell access to a Handbid auction and need to enroll each ticket buyer as a Handbid guest — for example, a box-office or RSVP tool integrating on behalf of one of our organizations. Available since Release 18.

One call — POST /v3/auction/{auctionId}/guests/register — replaces the create-then-check-in pair an integration might otherwise need. Each ticket buyer who opts in becomes a registered bidder with a paddle, and the Handbid welcome email is queued for them.

You get an organization API token instead of a manager login. Use it directly as the bearer on every call — there is no exchange step and no expiry to manage.

Authorization: Bearer <organization API token>
EnvironmentHost
Sandboxqa-rest.handbid.dev
Productionrest.hand.bid

Tokens differ per host, and a token only works on auctions belonging to your organization. A user OAuth2 bearer (the kind minted by the SMS-PIN flow) is never accepted here and is refused 401 — this route only accepts a raw organization API token.

POST https://<host>/v3/auction/<auctionId>/guests/register
Authorization: Bearer <organization API token>
Content-Type: application/json
{
"idempotencyKey": "acme:ticket:AC-2027-000123",
"email": "buyer@example.com",
"firstName": "Jane",
"lastName": "Doe",
"phone": "3055551234",
"phoneCountry": "US",
"externalRef": { "provider": "acme", "orderId": "AC-2027-000100", "ticketId": "AC-2027-000123" }
}
FieldRequiredNotes
auctionId (path)YesThe Handbid auction id for the event.
idempotencyKeyYesUp to 64 characters, letters/digits/:/_/-/.. Using your own ticket id (prefixed with your provider name, as above) is the recommended pattern — same ticket, same key, every time.
emailYesUp to 128 characters.
firstName, lastNameYesUp to 64 characters each.
phoneNoWhatever the buyer typed. Leave it out if they gave nothing.
phoneCountryNoTwo-letter ISO 3166-1 country code. Defaults to US.
externalRefNoYour own ids (provider, orderId, ticketId), stored for reconciliation.

Send only the fields listed here — unknown fields are rejected with a 400 so typos surface immediately. Booleans must be JSON true/false, never strings.

A 200 response:

{
"outcome": "created",
"guid": "3f2a1c9e-...",
"aurId": 6431510,
"userId": 3214094,
"paddleId": 118,
"state": { "isBidder": 1, "isCheckedin": 0, "participationStatusId": 1, "isDeleted": 0 },
"phone": { "accepted": true, "e164": "+13055551234", "reason": null },
"welcome": "published",
"checkedInAt": null,
"archived": false
}

outcome is created (new Handbid account, welcome email queued), linked (the buyer already had a Handbid account, or was already registered on this auction — same paddle, no second welcome email), or alreadyApplied (this key was already seen; nothing changed — the response then also carries originalOutcome, and phone.e164 is null on a replay because it is only echoed on the first write).

welcome tells you where the email stands: pending/published = queued (not yet delivered), sent = accepted by our mail provider, failed/unknown = we could not confirm delivery (resolved = a Handbid operator closed it without resending — we never resend automatically, so a buyer never gets two welcome emails from us). archived is true if you replay a key after the auction has been archived; ids still come back, and state is null once archiving has completed.

Ticket webhooks are frequently delivered more than once, and your own retries fire after timeouts. Both are safe:

  • Same key again → 200 with outcome: "alreadyApplied" and the original identifiers. No second bidder, no second paddle, no second email.
  • Same key with a different email, a different flag (registerBidder/sendWelcome/checkIn), or a different externalRef.ticketId → 409 conflict. Two different tickets are sharing an id on your side — do not retry; mint a new key.
  • Same key with a different name, phone, country, or externalRef.orderId → treated as a replay; the values from the first call stand (first-write-wins).
  • Two identical calls arriving at the same instant — the second may get 409 conflict with retryAfter: 2 and the message registration in progress. Retry with the same key after two seconds and you get alreadyApplied.

Retrying is always safe with the same key — no status code can ever cause a second registration.

WhenRetry with the same key?
Timeout / network errorYes
Any 5xx, or 503Yes, after retryAfter
429Yes, after retryAfter
409 with retryAfter (“registration in progress”)Yes, after retryAfter
400, 401, 403, 404No — fix the request or contact Handbid
409 without retryAfter (different email/flag/ticket)No — mint a new key

This is the recommended integration pattern, not just a failure fallback: replay the same call with the same key whenever you’re unsure whether the first one landed.

We normalize — you do not need to validate.

  • Send phone as typed and phoneCountry from your own country list. We parse the number as a national number of that country. A number starting with + is taken as international and overrides the country.
  • US and Canada: a bare 10-digit number is fine.
  • If we cannot make a real mobile number out of it, the buyer is still registered — we just store no phone. The response tells you: phone.accepted: false with reason of "invalid", "placeholder", or "absent".
  • A bad phoneCountry value (not a two-letter ISO code, or not a recognized one) is the one phone-related error that fails the whole call: 400.

All three default to the standard registration behavior:

  • registerBidder: false — add the person to the guest list without registering them as a bidder (no paddle, no email).
  • sendWelcome: false — skip the welcome email. Only meaningful when a new account is created; linking to an existing account never sends a second welcome email regardless of this flag.
  • checkIn: true — also mark the guest checked in. Not needed for a purely-online auction.

These flags are part of the idempotency key’s fingerprint — replaying a ticket with different flag values is a 409 conflict, not a silent change.

Plain JSON { "error": "...", "message": "..." }, per the site-wide error envelope.

StatuserrorMeaningRetry same key?
400validation_errorMissing, invalid, or unknown field — message says whichFix first
401unauthorizedToken missing, unknown, revoked, or a user OAuth2 bearerNo — contact Handbid
403forbiddenToken isn’t for this auction’s organization; partner registration isn’t enabled for the organization; or the organization’s bidder limit is reachedNo — contact Handbid
404not_foundUnknown auction id, or the auction has been archived (new key only)No
409conflictKey reused with a different email/flag/ticket (do not retry), or a concurrent registration still in flight (retryAfter, retry)See message
429rate_limitedSlow down — 120 requests/minute per organization token; retryAfter secondsYes, after retryAfter
503unavailableTransient; retryAfter secondsYes, after retryAfter
5xxserver_errorOursYes
  1. Get your sandbox organization API token and a sandbox auction id from your Handbid contact.
  2. Register one test buyer, register the same payload again, and confirm you get back alreadyApplied.
  3. Send one request with a garbage phone number and one with an international number plus its phoneCountry — check the phone block on each response.
  4. Send one request with a typo’d field name and confirm the 400 names it.
  5. Tell your Handbid contact the ticket id format you plan to use as idempotencyKey, so they can confirm it fits within 64 characters.

The complete field list, validation rules, and JSON examples for every response code live on the API reference page: POST /v3/auction/{auctionId}/guests/register.