Booking Transport

The API plugin owns only protected HTTP admission and byte transport. Extra Chill Events owns booking persistence, venue policy, authorization, idempotency, attachment storage and policy, one-time handoffs, delivery activity, and durable cleanup.

Submit An Inquiry

POST /wp-json/extrachill/v1/venues/{venue}/booking-inquiries

Anonymous, cookie-authenticated, and bearer-authenticated callers use the same Turnstile and atomic fixed-window rate-limit admission. Authentication is optional. When WordPress has validated a caller, Events reads that canonical current user from request context. Multipart and JSON affinity hops use the same signed internal-user transport. Form fields such as user_id, submitter_user_id, and uploader_user_id are rejected and never become authority.

JSON requests provide the inquiry fields directly. Multipart requests provide intake and attachment_purposes as JSON strings plus up to five attachments[] files. Transport limits are 20 MiB per file and 50 MiB in aggregate. Events applies the authoritative filename, MIME, purpose, scan, storage, and venue policy.

An exact retry with the same idempotency_key, fields, and ordered attachment bytes returns the same immutable receipt. Reusing the key with changed input returns booking_idempotency_conflict (409). The receipt contains only public_id, venue_term_id, and submitted_at.

Download An Attachment

GET /wp-json/extrachill/v1/events/bookings/{booking_id}/attachments/{attachment_id}/download

The caller must be authenticated and currently authorized by Events for the booking’s exact venue. The API issues and consumes an Events-owned one-time handoff, supports one byte range, and streams at most 20 MiB. HEAD and REST _envelope requests cannot consume a handoff.

Success is 200 or 206 with Content-Disposition, Content-Length, Content-Type, Accept-Ranges, and private no-store headers. The Events correlation is never exposed publicly or accepted from clients. For an affinity hop, nonce-bound internal response metadata transfers it to the outer worker and is stripped before the client response. Only that client-facing worker records completed, failed, interrupted, or partial after the actual stream outcome. Route-affinity spools are mode 0600, bounded, and removed after serving, failure, interruption, or shutdown.

Stable Errors

SituationCodeStatusClient action
REST field validationrest_missing_callback_param or rest_invalid_param400Use data.params and data.details as field paths.
Submitted user authoritybooking_identity_not_allowed400Remove all submitted user ID fields.
Invalid canonical identitybooking_authentication_invalid401Refresh or clear authentication, then retry.
Exact duplicate inquirySuccessful immutable receipt201Treat the returned public_id as the existing receipt.
Changed idempotent retrybooking_idempotency_conflict409Generate a new key only for an intentionally new inquiry.
Missing Turnstile tokenturnstile_missing_token403Render and submit a challenge.
Expired or invalid Turnstile tokenturnstile_failed403Refresh the challenge and retry.
Inquiry rate limitpublic_write_rate_limited429Wait for the Retry-After seconds.
Stale booking configurationbooking_inquiry_stale_config409Refresh configuration before resubmitting.
Intake disabled or unavailablebooking_inquiry_unavailable503Keep the draft and retry later.
Attachment count/upload mismatchbooking_attachment_count_invalid, booking_attachment_upload_failed, or booking_attachment_purpose_mismatch400Correct the multipart request.
Attachment too largebooking_attachment_size_invalid or booking_attachment_aggregate_size_invalid413Remove or reduce files.
Attachment policy rejectionbooking_attachment_rejected400 or 413Show the safe Events message next to the attachment.
Attachment storage or scan unavailablebooking_inquiry_unavailable503Keep the draft and retry later; internals remain hidden.
Uncertain inquiry attachment outcomebooking_inquiry_reconciliation_required503Do not change the idempotency key; retry only when reconciliation allows it.
Download unauthenticatedbooking_attachment_download_unavailable401Authenticate.
Download unauthorized, revoked, missing, expired, replayed, or tamperedbooking_attachment_download_unavailable404Do not reveal which condition occurred.
Download rate limitbooking_attachment_download_rate_limited429Wait for the Retry-After seconds.
Invalid or unsatisfiable rangebooking_attachment_range_unsatisfiable416Retry with one valid range; inspect Content-Range.
Unknown inquiry failurebooking_inquiry_unavailable503Keep the draft and retry later.
Unknown download failurebooking_attachment_download_unavailable502 or 503Retry later without exposing transport internals.

Only the explicit code/message/status/field contracts above are forwarded. An unknown domain error is always booking_inquiry_unavailable (503) regardless of its original status. Errors and logs must never contain temporary paths, storage roots, hashes, storage or object references, handoff tokens, delivery correlations, internal identities, or private bytes.