API reference

Events, orders and webhooks, for organisers, venues and professionals.

The Venued API is a small, deliberately narrow REST surface over data you already own on Venued. Every key belongs to one principal — an organiser account, a venue or a professional — and every response is filtered to it. GET /v1/me tells you which one a key is.

What this API deliberately cannot do

It cannot publish an event. POST and PATCH accept visibility of draft or unlisted only; anything else is refused with 403 forbidden. Publishing is a decision with a public consequence and a phone-verification gate behind it, so it stays in the dashboard where the person making it can see what they are about to make public.

⚠️ Note also that draft → unlisted is one-way. An event that has been made unlisted cannot be returned to draft through this API or any other.

It cannot cancel an event. Cancelling notifies everyone interested in it and leaves paid ticket holders needing refunds, which this API cannot issue. It stays in Studio, where the person doing it sees who is affected.

It cannot touch money. Orders are read-only. There is no refund, no payout and no ticket issuance endpoint.

It cannot write server-owned fields. Anything not on the writable list (see EventPatch) is silently dropped rather than rejected — including ticketing, organizerId, priority and the embedding fields.

Getting a key

API keys are created from Studio (Business account → API), not through this API. A key is shown once, at creation, and only its SHA-256 hash is stored — Venued cannot recover or re-display it. Creating and revoking keys requires the owner or org_manager role on an organiser account, and the owner role on a venue or a professional.

A key looks like vnd_live_ followed by 48 hex characters.

Who can hold a key

A key belongs to a principal — an organiser, a venue or a professional — and GET /v1/me tells you which. Authorisation is scope ∧ principal type, not scope alone, and the second half is not decoration:

  • Every business can hold an organiser key, whatever its kind. A venue or a professional is a business that organises too: its organiser key reads and writes the events it hosts, their orders and their attendees, exactly as any organiser's does. Its venue or professional keys come on top, for what it offers as a supplier.

  • events:read means my events to an organiser and the events at my address to a venue. Same scope string, different set.

  • A venue key cannot be minted with events:write or orders:read at all. A venue does not own the events booked into it, and the buyer names and emails on an order are the organiser's business, not the building's.

  • A venue can subscribe to webhooks, for event.published (an event at the venue's address became public, alongside the organiser's own delivery of the same event) and the supplier side of its deals: enquiry.received, enquiry.quoted, enquiry.countered, enquiry.accepted, enquiry.declined, enquiry.withdrawn, booking.confirmed and booking.cancelled. Never order.confirmed.

  • What a venue sees at its own address:

    • every public event;
    • an unlisted event only when no real account chose to unlist it — that is, when it belongs to an unclaimed placeholder business or to no organiser at all. Most unlisted events are catalogue rows collected from public sources that nobody has hidden from anyone; an unlisted event owned by an active organiser is that organiser's deliberate choice, and stays hidden;
    • never a draft, whoever owns it. A draft is an organiser's private working copy.

    Paging stays exact across that filter: has_more is observed rather than inferred, so a hidden row is never mistaken for the end of the list.

  • A professional key reads its own /v1/profile and /v1/offerings, the enquiries sent to it, and may subscribe to the same supplier-side deal events as a venue. It cannot hold events:*, orders:read or attendees:read.

  • attendees:read is organiser-only: ticket holders' contact details are the organiser's, never the venue's.

Conventions

  • Success bodies are {"data": …} — all of them.
  • List bodies add has_more and next_cursor beside data, on every list endpoint, including ones that cannot currently have a second page. One shape means one mapping in whatever tool you are wiring this into, rather than one per endpoint.
  • Error bodies are {"error": "<code>", "message": "<sentence>", "request_id": "<id>"}. message is for a human and may be reworded without notice; branch on error. request_id is on every response as the X-Request-Id header too — quote it when reporting a problem. Send your own and it is echoed rather than replaced, provided it is a plausible token of at most 64 characters.
  • Paging is by cursor. Pass ?limit= (max 100) and follow next_cursor into ?cursor=. A cursor is opaque and belongs to the query that produced it: it is refused with invalid_cursor if the ordering changed under it, or if the row it anchors on was deleted.
  • ?updated_after= is the poller's filter, on every list endpoint. ISO 8601 or epoch milliseconds, and it reorders the result by updatedAt ascending rather than the endpoint's default order — so changes arrive in the order they happened and the last updatedAt you saw is the only state you need to keep.
  • An unknown query parameter is a 400 unsupported_parameter, never silently ignored. A filter that looks applied but is not is how a poller quietly ships every row it meant to exclude.
  • Money is in minor units, as an integer, beside a non-null uppercase ISO-4217 currency. totalAmountMinor: 1999 is €19.99.
  • Concurrency: GET /v1/events/{eventId} returns an ETag. Send it back as If-Match on the PATCH and an edit that would overwrite somebody else's change fails with 412 precondition_failed instead. Omitting If-Match still succeeds.
  • HEAD is served wherever GET is: same status, same headers, no body. A 405 always carries an Allow header naming what the path does accept.
  • Timestamps are ISO 8601 strings in responses. Requests accept either ISO 8601 or epoch milliseconds.
  • title and description on an event are localized maps, not strings: {"EN": "Sunrise Run"}. Locale keys are normalised to UPPERCASE. The title in a response is already flattened to a single string.

Changes

The reference above describes the API as it is. What changed, and when, is kept here rather than threaded through it.

2026-09-21 — all breaking, all made while production held no API keys or webhook subscriptions, so nobody had anything to migrate:

  • Venue keys. A key belongs to a principal — an organiser or a venue — rather than always an organiser. See Who can hold a key.
  • Paging. Every list is ordered and pageable by cursor, with has_more and next_cursor. GET /v1/events was previously unordered, so an organiser with more than 100 events had no way to reach the rest.
  • Money. Order.totalAmount, a float in major units, became totalAmountMinor, an integer. The float is not kept alongside it.
  • DELETE /v1/webhooks/{webhookId} returns the {"data": …} envelope (it returned {"ok": true}, the only endpoint with its own shape) and is metered as a write (it was metered as a read).
  • Rate limits belong to the account, not the key. Minting a second key no longer doubles the budget.
  • Webhook envelopes name their principal instead of organizerId. A venue receiving organizerId: "<its venue id>" would have been wrong.

Version 1.0.0https://api.venued.eu/v1Download the OpenAPI document

Discovery

What this key can do.

GET/v1any valid key

What this key can reach

Returns the scopes on the calling key, the endpoint paths, the available webhook event types, the paging and filtering conventions and the current rate limits. Useful as a connectivity and permission check — it requires a valid key but no particular scope.

The endpoints map is derived from the route table the server dispatches from, not maintained beside it, so it cannot describe a surface the server does not serve.

Request

curl
curl https://api.venued.eu/v1/v1 \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Discovery document

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/meany valid key

Who this key is

The principal this key acts as, and the scopes it was minted with.

Why this is separate from GET /v1

GET /v1 answers what can I reach — the endpoint map, the paging conventions, the rate limits — and is the same for everybody. This answers who am I, which is per key and is the thing an integration branches on: a no-code step that must decide whether to offer event actions or venue actions needs the principal, not the endpoint table.

Requires a valid key but no particular scope. A key that cannot tell you what it is cannot be debugged by the person holding it, and "which account is this key for" is the first question anybody asks when an integration has written to the wrong place.

Used inAPI keys

Request

curl
curl https://api.venued.eu/v1/v1/me \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    The calling principal

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

Events

GET/v1/eventsevents:read

List events

Newest first, by startTime descending, and pageable with cursor.

Which events depends on the key: an organiser key lists the organiser's own events; a venue key lists the events at its address (see What a venue sees above).

⚠️ An event with no stored startTime is not returned. Nothing reachable is hidden by that — an event without a start is not on the site, in the app or in search either, and this API will not create one.

With updated_after the order changes to updatedAt ascending; see the conventions above.

Parameters

NameInTypeDescription
limitqueryinteger, 1–100, default 50

Clamped to 100. A missing, non-numeric, zero or negative value becomes the default of 50 rather than an error.

cursorquerystring

The next_cursor from the previous page. Opaque — do not construct, parse or store one as though it were an offset.

It is anchored to a specific row of a specific ordering, and it is rejected with 400 invalid_cursor rather than quietly obeyed when that no longer holds: if you add or remove updated_after (which changes the sort), or if the row it points at has since been deleted. Restart the pagination without a cursor in either case.

updated_afterquerystring

Only rows whose updatedAt is at or after this instant. ISO 8601 or epoch milliseconds.

⚠️ This changes the ordering to updatedAt ascending, on every endpoint that accepts it. That is what a poller wants — changes in the order they happened, so the last updatedAt processed is the whole of the state it has to keep — and it is also what lets the query be served by an index.

Request

curl
curl https://api.venued.eu/v1/v1/events \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Events owned by this organiser, newest first

  • 400

    unsupported_parameter — a query parameter this endpoint does not accept. invalid_cursor — a malformed cursor, one belonging to a different ordering, or one anchored to a row that no longer exists. invalid_argument — updated_after was not ISO 8601 or epoch ms.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

POST/v1/eventsevents:write

Create a draft or unlisted event

Creates an event owned by the calling organiser.

Idempotency-Key is required. A retried request carrying a key that already succeeded replays the stored response verbatim rather than creating a second event. A key whose first attempt FAILED is treated as fresh and re-run — only successful results are recorded — so a failed create is safe to retry with the same key. Keys are scoped per organiser and retained 7 days.

Parameters

NameInTypeDescription
Idempotency-Keyrequiredheaderstring

Any opaque string you generate per logical operation — a UUID is the usual choice. Scoped to your organiser account and retained 7 days.

Request

curl
curl -X POST https://api.venued.eu/v1/v1/events \
  -H "Authorization: Bearer vnd_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "title": {
    "EN": "Sunrise Run & Rave"
  },
  "startTime": "2026-11-21T18:00:00Z"
}'

Responses

  • 201

    Created

  • 400

    idempotency_key_required — no Idempotency-Key header. invalid_argument — startTime missing or unparseable, or endTime unparseable.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope, or forbidden when visibility is anything other than draft or unlisted.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/events/{eventId}events:read

Fetch one event

⚠️ An event belonging to a different organiser returns 404 not_found, not 403. That is deliberate: a 403 would confirm the id exists.

Parameters

NameInTypeDescription
eventIdrequiredpathstring

Request

curl
curl https://api.venued.eu/v1/v1/events/{eventId} \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    The event

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

PATCH/v1/events/{eventId}events:write

Update an event

Merges the writable fields of the body into the event. Fields outside the writable list are dropped silently, not rejected — so a body of nothing but server-owned keys is a 400 invalid_argument ("No writable fields in the request body"), which is usually the sign of a typo'd field name.

No Idempotency-Key is required: a PATCH of the same body is naturally idempotent.

If-Match makes the edit conditional. Send the ETag you got from GET /v1/events/{eventId} and the write applies only if nothing has changed since; otherwise it is refused with 412 precondition_failed and nothing is written. Without the header the write applies unconditionally, exactly as before — this is opt-in protection against two editors overwriting each other, not a new requirement.

Parameters

NameInTypeDescription
eventIdrequiredpathstring
If-Matchheaderstring

An ETag previously returned for this resource. The write applies only if the resource still matches; otherwise 412 precondition_failed and nothing is written. * means "it must exist". Omit it and the write is unconditional.

Request

curl
curl -X PATCH https://api.venued.eu/v1/v1/events/{eventId} \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    The updated event

  • 400

    invalid_argument — no writable fields in the body, or startTime / endTime unparseable.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope, or forbidden when visibility is anything other than draft or unlisted.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 412

    precondition_failed — the If-Match you sent does not match the resource's current ETag, so somebody else changed it after you read it. Nothing was written. Re-read the resource, reapply your change to the new version, and retry.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

Orders

GET/v1/events/{eventId}/ordersorders:read

Confirmed orders for one event

Only orders with status: "confirmed" are returned — pending, failed and abandoned checkouts are not visible through this API.

Newest first, by createdAt descending, and pageable with cursor.

Parameters

NameInTypeDescription
eventIdrequiredpathstring
limitqueryinteger, 1–100, default 50

Clamped to 100. A missing, non-numeric, zero or negative value becomes the default of 50 rather than an error.

cursorquerystring

The next_cursor from the previous page. Opaque — do not construct, parse or store one as though it were an offset.

It is anchored to a specific row of a specific ordering, and it is rejected with 400 invalid_cursor rather than quietly obeyed when that no longer holds: if you add or remove updated_after (which changes the sort), or if the row it points at has since been deleted. Restart the pagination without a cursor in either case.

updated_afterquerystring

Only rows whose updatedAt is at or after this instant. ISO 8601 or epoch milliseconds.

⚠️ This changes the ordering to updatedAt ascending, on every endpoint that accepts it. That is what a poller wants — changes in the order they happened, so the last updatedAt processed is the whole of the state it has to keep — and it is also what lets the query be served by an index.

Request

curl
curl https://api.venued.eu/v1/v1/events/{eventId}/orders \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Confirmed orders for this event, newest first

  • 400

    unsupported_parameter — a query parameter this endpoint does not accept. invalid_cursor — a malformed cursor, one belonging to a different ordering, or one anchored to a row that no longer exists. invalid_argument — updated_after was not ISO 8601 or epoch ms.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/events/{eventId}/attendeesattendees:read

Everyone holding a ticket to one event, with contact details

One row per ticket, the unit the door scans, joined to its order: the buyer's name and email, and every answer to the event's checkout questions. A phone number appears here when you asked for one as a question; Venued holds no other phone for a buyer.

Nothing is filtered out. Refunded, transferred-away and cancelled tickets come back with voided: true, so a door list rebuilt from this is complete and a count can say what it excluded. People who hid themselves from the public guest list are included: that setting is about other guests, not about you.

Newest first by issue date; updated_after flips it to ascending on updatedAt, which a check-in or a refund also moves.

⚠️ This is personal data about your guests. It leaves Venued the moment a key reads it, so keep the key where your guest list already lives.

Parameters

NameInTypeDescription
eventIdrequiredpathstring
limitqueryinteger, 1–100, default 50

Clamped to 100. A missing, non-numeric, zero or negative value becomes the default of 50 rather than an error.

cursorquerystring

The next_cursor from the previous page. Opaque — do not construct, parse or store one as though it were an offset.

It is anchored to a specific row of a specific ordering, and it is rejected with 400 invalid_cursor rather than quietly obeyed when that no longer holds: if you add or remove updated_after (which changes the sort), or if the row it points at has since been deleted. Restart the pagination without a cursor in either case.

updated_afterquerystring

Only rows whose updatedAt is at or after this instant. ISO 8601 or epoch milliseconds.

⚠️ This changes the ordering to updatedAt ascending, on every endpoint that accepts it. That is what a poller wants — changes in the order they happened, so the last updatedAt processed is the whole of the state it has to keep — and it is also what lets the query be served by an index.

Request

curl
curl https://api.venued.eu/v1/v1/events/{eventId}/attendees \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    A page of attendees.

  • 400

    unsupported_parameter — a query parameter this endpoint does not accept. invalid_cursor — a malformed cursor, one belonging to a different ordering, or one anchored to a row that no longer exists. invalid_argument — updated_after was not ISO 8601 or epoch ms.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/ordersorders:read

Confirmed orders across all of this organiser's events

Newest first (createdAt descending). Confirmed orders only — pending, failed and abandoned checkouts are never visible here.

This is the poll source a "new order" trigger in Zapier or Make needs; the order.confirmed webhook is the push counterpart. A robust integration usually wants both: the webhook for latency, and ?updated_after= to backfill whatever the receiver was down for.

Parameters

NameInTypeDescription
limitqueryinteger, 1–100, default 50

Clamped to 100. A missing, non-numeric, zero or negative value becomes the default of 50 rather than an error.

cursorquerystring

The next_cursor from the previous page. Opaque — do not construct, parse or store one as though it were an offset.

It is anchored to a specific row of a specific ordering, and it is rejected with 400 invalid_cursor rather than quietly obeyed when that no longer holds: if you add or remove updated_after (which changes the sort), or if the row it points at has since been deleted. Restart the pagination without a cursor in either case.

updated_afterquerystring

Only rows whose updatedAt is at or after this instant. ISO 8601 or epoch milliseconds.

⚠️ This changes the ordering to updatedAt ascending, on every endpoint that accepts it. That is what a poller wants — changes in the order they happened, so the last updatedAt processed is the whole of the state it has to keep — and it is also what lets the query be served by an index.

Request

curl
curl https://api.venued.eu/v1/v1/orders \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Confirmed orders, newest first

  • 400

    unsupported_parameter — a query parameter this endpoint does not accept. invalid_cursor — a malformed cursor, one belonging to a different ordering, or one anchored to a row that no longer exists. invalid_argument — updated_after was not ISO 8601 or epoch ms.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

Venues

For a venue key — the place, and what happens at it.

GET/v1/venuesevents:read

The venue this key is, and the places it is

A venue key's own record, with its locations resolved.

⚠️ Always exactly one entry today. A venue key's principal IS a venue, so "list" is one row. It carries the page envelope anyway, for the same reason every other list does: one mapping in a no-code tool beats one per endpoint.

url is built from the venue id, not its slug. Venue slug uniqueness is not enforced — the Studio editor writes the field directly — so a slug URL can resolve to a different venue. The slug is published as a field for display; it is not the link.

Request

curl
curl https://api.venued.eu/v1/v1/venues \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    The venue, with its locations

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

Enquiries

For a venue key — who is asking to hire it, and which days are already taken. Read-only: answering an enquiry is a negotiation with money at the end of it, and that stays in Studio where a person is looking at it.

GET/v1/enquiriesenquiries:read

Who is asking to hire this venue

Every enquiry addressed to this venue, most recently active first.

An enquiry is the conversation BEFORE a booking: an organiser describes what they need, proposes dates, and the two sides quote and counter until somebody accepts. Accepting creates the booking, so money stays in one place; this endpoint never sees that part.

⚠️ A draft is never returned. An enquiry an organiser has written and not sent is private to their side. The filter runs per page and the page stays exact: has_more is observed, so a hidden row is never mistaken for the end of the list.

⚠️ Addressed by the place as well as the venue. An enquiry raised against an address before that address was attached to this venue still belongs here, so both are matched. A venue operating more than 29 places sees the first 29 of them plus everything addressed to the venue itself, which is the same limit the dashboard's own inbox works under.

updated_after filters on the activity clock and flips the sort to ascending, so a poller reads changes in the order they happened.

Parameters

NameInTypeDescription
limitqueryinteger, 1–100, default 50

Clamped to 100. A missing, non-numeric, zero or negative value becomes the default of 50 rather than an error.

cursorquerystring

The next_cursor from the previous page. Opaque — do not construct, parse or store one as though it were an offset.

It is anchored to a specific row of a specific ordering, and it is rejected with 400 invalid_cursor rather than quietly obeyed when that no longer holds: if you add or remove updated_after (which changes the sort), or if the row it points at has since been deleted. Restart the pagination without a cursor in either case.

updated_afterquerystring

Only rows whose updatedAt is at or after this instant. ISO 8601 or epoch milliseconds.

⚠️ This changes the ordering to updatedAt ascending, on every endpoint that accepts it. That is what a poller wants — changes in the order they happened, so the last updatedAt processed is the whole of the state it has to keep — and it is also what lets the query be served by an index.

Request

curl
curl https://api.venued.eu/v1/v1/enquiries \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Enquiries for this venue, most recently active first

  • 400
  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

POST/v1/enquiriesenquiries:write

Put an inquiry from your own channels into your Venued inbox

For the forms and tools a business already uses to collect inquiries: its website form, a Typeform, a Zapier or Make scenario reading a booking portal's mails. The inquiry lands in the business's Venued inbox exactly as a forwarded mail does: unread, with a notice, as a deal with an outside client the business then quotes from Studio.

It creates, it never answers. Quoting, accepting and declining stay in Studio, where a person looks at the money.

Whose inbox is the key's. A venue key may name one of its own places with locationId (else its first place); a professional key is itself and may name one of its services with serviceId.

Idempotency-Key is required, as for every create: a retried call with a key that already succeeded replays the stored response.

Parameters

NameInTypeDescription
Idempotency-Keyrequiredheaderstring

Any opaque string you generate per logical operation — a UUID is the usual choice. Scoped to your organiser account and retained 7 days.

Request

curl
curl -X POST https://api.venued.eu/v1/v1/enquiries \
  -H "Authorization: Bearer vnd_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "title": "Hochzeit Hofmann",
  "client": {
    "name": "Lena Hofmann",
    "email": "lena@example.de",
    "phone": "+49 151 2345678"
  },
  "need": {
    "role": "Hochzeit, Abendessen und Party",
    "headcount": 80,
    "brief": "Samstag, gern mit DJ."
  },
  "dates": [
    {
      "start": "2027-06-12",
      "allDay": true
    }
  ],
  "reference": "form-4711"
}'

Responses

  • 201

    Created, unread in the business's inbox

  • 400

    idempotency_key_required — no Idempotency-Key header. invalid_argument — no contact (give client.email or client.phone), no title, no need.role, or a date that does not parse; or this venue has no place yet.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key lacks enquiries:write. forbidden — not a venue or professional key.

  • 404

    not_found — locationId is not one of this venue's places, or the business no longer exists.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/enquiries/{enquiryId}enquiries:read

One enquiry

⚠️ 404, not 403, for an enquiry belonging to another venue. Whether a given id exists is not something this key should be able to confirm. A draft answers the same way, for the same reason the list omits it.

Used inWebhooks

Parameters

NameInTypeDescription
enquiryIdrequiredpathstring

Request

curl
curl https://api.venued.eu/v1/v1/enquiries/{enquiryId} \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    The enquiry

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/availabilityavailability:read

Which days this venue is already taken

The days this venue is known to be busy, and the subset of them it blocked by hand.

⚠️ "Busy" is known; "free" is not a promise. The list is derived from bookings that hold a date, manual blocks and the venue's own events, and it is deliberately optimistic: a venue Venued knows nothing about reads as available, because search treats an unknown as bookable rather than hiding it. A day that is absent here therefore means we have no reason to think it is taken, which is not the same as free. Confirm before promising it.

⚠️ Whole days, not hours. A venue booked for one lunch is busy for that day as far as this index is concerned. Timed holds are coming; the day list stays, because a day list is what a subscribed calendar can consume.

⚠️ Per place. These fields live on each location, because a venue with three rooms has three calendars. An earlier version of this endpoint read them off the venue record, where nothing writes them, and so answered with empty lists for everybody.

manualBlockedDates is separated out because it is the only part the venue authored — a booking-derived day is a fact, a manual block is a decision, and a calendar sync needs to know which of its own entries it is looking at.

Request

curl
curl https://api.venued.eu/v1/v1/availability \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    The days known to be taken

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

Professionals

A professional's own profile and offerings, for its key only. Read-only: the profile is edited in Studio.

GET/v1/profileprofile:read

This professional's own profile

The profile the key belongs to, including its own contact fields (which the public page shows only as the professional chose). url is built from the id and redirects to the current slug.

Request

curl
curl https://api.venued.eu/v1/v1/profile \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    The profile.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/offeringsofferings:read

This professional's offerings, in every status

Drafts, paused and archived offerings included, since the key is the owner's; status says which. The public page shows published only.

Ordered as in Studio (sortOrder, then title). A professional has a handful, so this is one page: has_more is always false.

Request

curl
curl https://api.venued.eu/v1/v1/offerings \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Every offering.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

Webhooks

GET/v1/webhookswebhooks:read

List webhook subscriptions

⚠️ The secret is returned here in plaintext. Treat this response as a credential.

Used inWebhooks

Request

curl
curl https://api.venued.eu/v1/v1/webhooks \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Subscriptions

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

POST/v1/webhookswebhooks:write

Subscribe to events

Creates a subscription and returns its signing secret. Store the secret now — it is also readable from GET /v1/webhooks, but it is what verifies every delivery and it should be treated as a credential from the moment it exists.

A maximum of 10 subscriptions per account is enforced.

A venue key may subscribe to event.published and the supplier side of its deals (enquiry.received, enquiry.quoted, enquiry.countered, enquiry.accepted, enquiry.declined, enquiry.withdrawn, booking.confirmed, booking.cancelled); a professional key to the same deal events without event.published. enquiry.sent and invoice.issued belong to the business account and need an organiser key. order.confirmed is refused with 403 forbidden rather than silently dropped: it carries a buyer's name and email, which is the organiser's record and not the building's, and a subscription quietly narrower than the one you asked for would only announce itself by never firing.

Used inWebhooks

Request

curl
curl -X POST https://api.venued.eu/v1/v1/webhooks \
  -H "Authorization: Bearer vnd_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "url": "https://example.com/hooks/venued",
  "events": [
    "order.confirmed"
  ],
  "description": "Push confirmed orders into the CRM"
}'

Responses

  • 201

    Created. Carries the signing secret.

  • 400

    invalid_argument — url missing, unparseable, not https, or not a public host; or events contains no known type.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope, or forbidden when a venue or professional key asks for an event type it may not receive (see above).

  • 409

    limit_reached — already 10 subscriptions.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

PATCH/v1/webhooks/{webhookId}webhooks:write

Change a webhook subscription

Send any of url, events, enabled and description. Fields you leave out are unchanged. The secret cannot be set here.

Strict on purpose, because a lenient reading of these fields fails silently:

  • enabled must be a JSON boolean. The string "true" is a 400, not a quiet "off".
  • Any other field, secret included, is a 400, never ignored.
  • events follows the same rules as on create. A type your key's account may not receive is a 403 naming it, not dropped.

Re-enabling an endpoint (enabled: true) resets consecutiveFailures to 0. An endpoint that auto-disabled after 20 failures comes back this way once you have fixed it.

Used inWebhooks

Parameters

NameInTypeDescription
webhookIdrequiredpathstring

Request

curl
curl -X PATCH https://api.venued.eu/v1/v1/webhooks/{webhookId} \
  -H "Authorization: Bearer vnd_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true
}'

Responses

  • 200

    The subscription as it now is.

  • 400

    invalid_argument: an empty body, an unknown field, a non-boolean enabled, a bad url, or no known event type.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope, or forbidden for an event type this account may not receive.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

DELETE/v1/webhooks/{webhookId}webhooks:write

Delete a webhook subscription

Removes the subscription and returns {"data": {"id": "…", "deleted": true}}. Deliveries already in flight are not recalled.

Metered as a write (120/min), like every other request that changes something.

Used inWebhooks

Parameters

NameInTypeDescription
webhookIdrequiredpathstring

Request

curl
curl -X DELETE https://api.venued.eu/v1/v1/webhooks/{webhookId} \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    Deleted

  • 400

    invalid_argument — no webhookId in the path.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

GET/v1/webhooks/{webhookId}/deliverieswebhooks:read

List an endpoint's recent deliveries

What was sent to this endpoint and what came back, newest first. One row per delivery: retries of it update the same row. Rows are kept for 30 days.

Use it to find the delivery your receiver missed, then resend it.

Used inWebhooks

Parameters

NameInTypeDescription
webhookIdrequiredpathstring
limitqueryinteger, 1–100, default 50

Clamped to 100. A missing, non-numeric, zero or negative value becomes the default of 50 rather than an error.

cursorquerystring

The next_cursor from the previous page. Opaque — do not construct, parse or store one as though it were an offset.

It is anchored to a specific row of a specific ordering, and it is rejected with 400 invalid_cursor rather than quietly obeyed when that no longer holds: if you add or remove updated_after (which changes the sort), or if the row it points at has since been deleted. Restart the pagination without a cursor in either case.

Request

curl
curl https://api.venued.eu/v1/v1/webhooks/{webhookId}/deliveries \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 200

    A page of deliveries.

  • 400

    unsupported_parameter — a query parameter this endpoint does not accept. invalid_cursor — a malformed cursor, one belonging to a different ordering, or one anchored to a row that no longer exists. invalid_argument — updated_after was not ISO 8601 or epoch ms.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

POST/v1/webhooks/{webhookId}/deliveries/{deliveryId}/resendwebhooks:write

Resend one delivery

Queues the stored delivery again, byte for byte, with the same id. A receiver that deduplicates on the delivery id, as it should, treats a resend of a delivery it already processed as a duplicate. That is also why this needs no Idempotency-Key: calling it twice sends the same delivery twice, which your receiver already has to handle.

Returns 202 once the delivery is queued, not once it has arrived. Watch the delivery's row in the log for the outcome.

Used inWebhooks

Parameters

NameInTypeDescription
webhookIdrequiredpathstring
deliveryIdrequiredpathstring

Request

curl
curl -X POST https://api.venued.eu/v1/v1/webhooks/{webhookId}/deliveries/{deliveryId}/resend \
  -H "Authorization: Bearer vnd_live_your_key_here"

Responses

  • 202

    Queued.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found: no such webhook, or no such delivery (the log keeps 30 days).

  • 409

    webhook_disabled: the endpoint is switched off. Enable it first.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

  • 503

    unavailable: the delivery could not be queued. Retry shortly.

POST/v1/webhooks/{webhookId}/rotate_secretwebhooks:write

Rotate the signing secret, with an overlap

Issues a new signing secret and returns it. The old one keeps signing for grace_seconds (default 3600, at most 604800, 0 to switch at once), so for that window every delivery carries two v1 values and verifies on either. Put the new secret in your receiver within the window.

Idempotency-Key is required. Only one previous secret is kept, so a rotation during an open overlap ends the older secret at once. A retried rotate without a key would mint a second secret and silently retire the one your receiver still uses. With a key, the retry returns the first response, secret included.

Used inWebhooks

Parameters

NameInTypeDescription
webhookIdrequiredpathstring
Idempotency-Keyrequiredheaderstring

Any opaque string you generate per logical operation — a UUID is the usual choice. Scoped to your organiser account and retained 7 days.

Request

curl
curl -X POST https://api.venued.eu/v1/v1/webhooks/{webhookId}/rotate_secret \
  -H "Authorization: Bearer vnd_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "grace_seconds": 3600
}'

Responses

  • 200

    The subscription with its new secret.

  • 400

    idempotency_key_required: no Idempotency-Key header. invalid_argument: grace_seconds out of range or not a whole number, or any other field.

  • 401

    unauthorized — missing, malformed, unknown or revoked key.

  • 403

    insufficient_scope — the key is valid but lacks the scope.

  • 404

    not_found. Also returned for a resource that exists but belongs to a different organiser — see the note on GET /v1/events/{eventId}.

  • 429

    rate_limited. 600 reads/min and 120 writes/min in a fixed 60-second window. POST, PATCH and DELETE are writes; GET and HEAD are reads.

    ⚠️ The budget belongs to the organiser account, not to the key. It was metered per key, which meant minting a second key doubled the allowance — so there was no ceiling, only a formality. Two changes, both breaking, both made while the API has no keys in existence: this one, and DELETE moving from the read budget to the write budget.

    ⚠️ The limit is a soft ceiling under burst. The counter is a single Firestore document per key per window, and contention means a hard burst can push somewhat past the nominal figure before 429s begin. It also fails open: if the counter cannot be read, the request is allowed rather than refused.

Webhook events

These are sent to you: Venued POSTs the envelope below to every endpoint subscribed to that event type.

Every delivery carries

FieldTypeDescription
idstring, uuid

The delivery id. Use it to deduplicate.

typeorder.confirmed | event.published | enquiry.received | enquiry.sent | enquiry.quoted | enquiry.countered | enquiry.accepted | enquiry.declined | enquiry.withdrawn | booking.confirmed | booking.cancelled | invoice.issued
createdAtstring, date-time
principalobject

Who this delivery is for — an organiser, or the venue an event is happening at.

⚠️ A delivery resent from before 2026-09-21 reproduces its original body, which carried organizerId instead. See Changes.

principal.typeorganiser | venue | professional
principal.idstring
dataobject
EVENTorder.confirmed

A ticket order or RSVP was confirmed

Sent once, on the pending → confirmed transition of an order. It does not re-fire if the order is written again while already confirmed.

EVENTevent.published

An event became public

Sent once, the first time an event transitions to public. Making an already-public event public again does not re-fire it.

EVENTenquiry.received

Somebody asked to hire this venue or professional

Sent once, when an enquiry reaches the business — the moment it stops being the organiser's unsent draft. A later edit does not re-fire it.

⚠️ Not for a deal you logged yourself. A business can log an enquiry it took by phone from an outside client; posting a webhook back for that would be telling you what you just did, so it is not delivered. An enquiry that arrived is delivered, whoever sent it: an organiser on Venued, the inquiry form on your own website, or a mail to your intake address.

⚠️ No contact details, deliberately. The payload says an enquiry exists and what it is for. Names, emails and phone numbers are not in it, because a webhook goes to a URL that is usually a third-party tool and is retried and replayable. Read the enquiry with GET /v1/enquiries/{enquiryId}, which is authenticated and scoped.

Deliverable to the venue or professional the enquiry is addressed to, and to the business account that runs it. data.side is always supplier; the organiser's side of the same moment is enquiry.sent. Since 2026-10-04 the payload also carries side, acceptedDate and quote (both null here); see EnquiryWebhookData.

EVENTenquiry.sent

Your business sent an enquiry to a venue or professional

Sent once, when an enquiry your business wrote stops being a draft and reaches the business it is addressed to. The organiser's side of the moment the venue or professional receives enquiry.received.

Organiser keys only (any kind of business can hold one).

EVENTenquiry.quoted

The business sent an offer

Sent when the venue or professional sends a new offer (a new price revision). Delivered to both sides: the organiser that asked, and the business that quoted, so an integration on either side holds the current price. data.side says which side a delivery is for, and data.quote carries the price.

EVENTenquiry.countered

The organiser countered an offer

Sent when the organiser answers an offer with a counter-offer (a new price revision). Delivered to both sides; data.quote is the counter.

EVENTenquiry.accepted

An offer was accepted

Sent once, when the enquiry becomes accepted, whoever accepted. Delivered to both sides. data.acceptedDate is the agreed date and data.quote the agreed price. The booking that follows is booking.confirmed.

EVENTenquiry.declined

The business declined an enquiry

Sent once, when the venue or professional declines. Delivered to both sides.

EVENTenquiry.withdrawn

The organiser withdrew an enquiry

Sent once, when an enquiry that had been sent is withdrawn. Withdrawing an unsent draft sends nothing: it never existed for the business. Delivered to both sides.

EVENTbooking.confirmed

A booking was agreed

Sent once, the first time a booking reaches an agreed state (confirmed, event_linked or completed; a booking can land past confirmed in one step, and is announced once either way).

Delivered to the organiser that booked, and to the supplier: the business account that is the merchant of record, and the venue or professional booked. data.side says which. One business on both sides (a venue booking its own room) receives one of each.

EVENTbooking.cancelled

A booking was cancelled

Sent once, when a booking becomes cancelled, to the same parties as booking.confirmed.

EVENTinvoice.issued

Your business issued an invoice

Sent once per number, when your business issues a document from its invoice book: an invoice, an advance invoice, a final invoice, or a cancellation (kind: invoice, advance, final, storno).

Organiser keys only: the invoice book belongs to the business account, not to a venue or professional record.

⚠️ No buyer in the payload. The buyer is often a private person; their name, email and address are not sent to a webhook URL.