Perfect Venue REST API Docs (1.3.0)

Download OpenAPI specification:Download

Perfect Venue Engineering: eng@perfectvenue.com URL: https://www.perfectvenue.com License: Terms of Service

Introduction

Welcome to the Perfect Venue API docs. This API is available to Perfect Venue partners. If you're interested in building an integration, please reach out to eng@perfectvenue.com and we'll help you get set up.

This site was generated with Redoc and the OpenAPI/Swagger format.

Quick start

Use this flow when building a new integration:

  1. Contact Perfect Venue to provision OAuth access for your integration.
  2. Perfect Venue will configure your OAuth application and provide client credentials to your partner developer team.
  3. Send a Perfect Venue user with access to the relevant venues through the OAuth authorization code flow.
  4. Exchange the authorization code for an access token and refresh token.
  5. Call GET /venues to find the venues available to the authorizing Perfect Venue user.
  6. Store the Perfect Venue venue_id values in your system.
  7. Create events with POST /events, including contact details inline when you do not already have a contact_id.
  8. Use GET /events plus webhooks to keep your system in sync.

All authenticated API requests use this base URL:

https://api.perfectvenue.com/api/v1

Implementation path

1. List venues

After a Perfect Venue user authorizes your integration, call GET /venues first. Venue IDs are required when creating events. The venue response also includes venue-specific configuration such as event types, spaces, and custom fields.

curl https://api.perfectvenue.com/api/v1/venues \
  -H "Authorization: Bearer ACCESS_TOKEN"

Save the id for each venue your integration should write to. We recommend mapping each Perfect Venue venue_id to the equivalent resource in your system, such as a location, merchant, store, or account.

2. Create an event

Create an event with a known contact_id, or pass a top-level contact object to create or reuse a contact inline. Use start_time and end_time for new integrations; start_date and end_date are deprecated.

curl https://api.perfectvenue.com/api/v1/events \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event": {
      "venue_id": "VENUE_ID",
      "name": "Henry's Wedding",
      "start_time": "2026-08-15T18:00:00Z",
      "end_time": "2026-08-15T23:00:00Z",
      "group_size": 120,
      "source": "Partner Integration",
      "event_type": "Wedding",
      "spaces": ["main-hall"],
      "custom": {
        "dietary_restrictions": "vegan"
      }
    },
    "contact": {
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@example.com",
      "phone": "+14155550100"
    },
    "message": {
      "text": "Interested in a Saturday evening reception."
    }
  }'

3. Sync events

Use GET /events to backfill or reconcile data. Always pass venue_id; use pagination for large result sets.

curl "https://api.perfectvenue.com/api/v1/events?venue_id=VENUE_ID&per_page=100&page=1" \
  -H "Authorization: Bearer ACCESS_TOKEN"

For incremental syncs, filter by updated_at:

curl "https://api.perfectvenue.com/api/v1/events?venue_id=VENUE_ID&date_field=updated_at&date_from=2026-06-01T00:00:00Z&date_to=2026-06-30T23:59:59Z" \
  -H "Authorization: Bearer ACCESS_TOKEN"

Data model notes

  • venue_id identifies a venue available to the authorizing Perfect Venue user and is required for creating contacts and events.
    • contact_id identifies the event's primary contact.
    • event_type must match an event type configured for the venue.
    • spaces should contain space slugs from the venue's spaces array.
    • custom values are keyed by the venue's custom field key.
    • Event timestamps are ISO 8601 strings. Use the venue's timezone when displaying local times to users.
    • Money amounts are integer cents, for example 150000 for $1,500.00.

Error handling

Most validation, permission, and not-found errors use status: "fail" and include one or more messages in data.

{
  "status": "fail",
  "data": [
    { "message": "End time must be after start time" }
  ]
}

Common error statuses:

  • 400 Bad Request: validation failed or required parameters are missing.
  • 401 Unauthorized: the access token is missing, invalid, or expired.
  • 403 Forbidden: the token does not grant access to the requested venue or resource.
  • 404 Not Found: the requested resource does not exist or is not visible to the authorizing Perfect Venue user.
  • 429 Too Many Requests: the access token exceeded the rate limit. Rate-limit responses use a problem-details style JSON body with title, status, and detail.

Authentication

The Perfect Venue API uses OAuth 2.0 Bearer tokens for authentication. Partner developers build and operate integrations, but a Perfect Venue user must log in and authorize the application before the integration can access venue data.

At a high level:

  1. Perfect Venue provisions an OAuth application for your integration.
  2. Your integration redirects a Perfect Venue user to the authorization URL.
  3. The user logs in and grants permission.
  4. Perfect Venue redirects the user back to your redirect_uri with an authorization code.
  5. Your server exchanges that code for an access token and refresh token.
  6. Your integration uses the access token when calling the API.

Access is limited to the Perfect Venue user who authorized the application. For example, if that user can access two venues, GET /venues returns those venues and API writes are limited to resources that user can access.

OAuth credentials

Perfect Venue provides the following values during setup:

Value Description
client_id Public identifier for your OAuth application
client_secret Secret used by your server when exchanging authorization codes and refresh tokens
redirect_uri Callback URL registered for your integration

Keep the client_secret on your server. Do not expose it in browser code, mobile apps, logs, or customer-visible configuration.

Authorizing the app

Direct a Perfect Venue user to the Authorization URL with the following query parameters. The user must log in and grant permission before your integration can receive an authorization code.

  • response_type: Set to "code"
  • client_id: Your application's unique client ID, provided by Perfect Venue
  • redirect_uri: The URL to which the user will be redirected after authorization
  • state: (Optional but recommended) A random string to maintain state between the request and callback, helping to prevent CSRF attacks

Example Authorization URL:

  https://api.perfectvenue.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&state=RANDOM_STRING

Always validate the state value on the callback before exchanging the authorization code.

Obtaining an access token

Once the Perfect Venue user has authorized your application, they will be redirected to the specified redirect_uri with a code parameter in the query string. If authorization fails or the user declines access, the redirect may include an OAuth error instead of code.

To exchange this authorization code for an access token, send a POST request to the Token URL with the following setup:

  1. Authorization Header: Use Basic HTTP authentication, where the client_id and client_secret are combined in the format client_id:client_secret and Base64 encoded. This encoded value is included in the Authorization header as Basic <encoded_credentials>.
  2. Body Parameters (URL-encoded)
    • grant_type: Set to "authorization_code"
    • code: The authorization code received in the redirect
    • redirect_uri: Must match the redirect_uri used the previous step

Example Token Request:

  POST https://api.perfectvenue.com/oauth/token
  Authorization: Basic BASE64_ENCODED(client_id:client_secret)
  Content-Type: application/x-www-form-urlencoded

  grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI

Example Token Response:

{
  "access_token": "ACCESS_TOKEN",
  "token_type": "Bearer",
  "expires_in": 7200,
  "refresh_token": "REFRESH_TOKEN",
  "created_at": 1782950400
}

Store tokens securely. Access tokens are short-lived. Refresh tokens should be treated like passwords because they can be exchanged for new access tokens.

Using access tokens

Upon a successful token exchange, you’ll receive a JSON response with an access token. Include this token in the Authorization header as Bearer <token> for all authenticated requests.

Access tokens represent the Perfect Venue user who authorized the application. API responses and writes are limited to the venues and resources that user can access in Perfect Venue.

Example API Request:

  GET /api/v1/venues
  Host: api.perfectvenue.com
  Authorization: Bearer YOUR_ACCESS_TOKEN

Using refresh tokens

Token responses will include a refresh token that can be exchanged for a new access token.

To exchange a refresh token for a new access token, send a POST request to the Token URL with the following setup:

  1. Authorization Header: Use Basic HTTP authentication, where the client_id and client_secret are combined in the format client_id:client_secret and Base64 encoded. This encoded value is included in the Authorization header as Basic <encoded_credentials>.
  2. Body Parameters (URL-encoded)
    • grant_type: Set to "refresh_token"
    • refresh_token: The refresh token obtained from a previous token request

Example Refresh Token Request:

  POST https://api.perfectvenue.com/oauth/token
  Authorization: Basic BASE64_ENCODED(client_id:client_secret)
  Content-Type: application/x-www-form-urlencoded

  grant_type=refresh_token&refresh_token=REFRESH_TOKEN

Your integration should refresh the token before it expires or when an API request returns 401 Unauthorized because the access token is expired. If a refresh request fails, send the Perfect Venue user through the authorization flow again.

Revoking tokens

Use token revocation when a user disconnects your integration, when you rotate stored credentials, or when you no longer need access for a Perfect Venue user.

Send a POST request to the revocation endpoint with client authentication and the token you want to revoke:

  POST https://api.perfectvenue.com/oauth/revoke
  Authorization: Basic BASE64_ENCODED(client_id:client_secret)
  Content-Type: application/x-www-form-urlencoded

  token=ACCESS_OR_REFRESH_TOKEN

Request parameters:

Parameter Required Description
token Yes Access token or refresh token to revoke
token_type_hint No Optional hint such as access_token or refresh_token

Successful revocation returns 200 OK with an empty JSON body. The endpoint also returns 200 OK for missing, invalid, or already revoked tokens. If the token was issued to a different OAuth client, Perfect Venue returns 403 Forbidden with unauthorized_client.

After revoking a token, remove it from your system. If your integration needs access again, send the Perfect Venue user through the OAuth authorization flow to receive new tokens.

Authentication errors

Status Meaning What to do
401 Unauthorized The access token is missing, invalid, expired, or revoked Refresh the token or send the user through OAuth again
403 Forbidden The token is valid, but the authorizing user does not have access to the requested venue or resource Ask the user to confirm their Perfect Venue permissions or authorize with a different user

Rate limiting

The API is rate limited to 100 requests per minute per access token. If you exceed this limit, the API will respond with 429 Too Many Requests.

When a request is rate limited, the response includes headers that help you decide when to retry:

Header Description
Retry-After Seconds to wait before retrying
X-RateLimit-Limit Maximum requests allowed per minute
X-RateLimit-Remaining Requests remaining in the current window
X-RateLimit-Reset Unix timestamp when the current limit window resets

When you receive a 429, wait before retrying. For API support, contact eng@perfectvenue.com.

Contacts

Create contact

Creates a new contact.

Perfect Venue does not enforce unique fields on contacts. If you want to avoid creating contacts with duplicate phone numbers or emails, we recommend storing a copy of the contact ID in your own database, and enforce unique fields there.

Authorizations:
bearerHttpAuthentication
Request Body schema: application/json
required
required
object (CreateContactInput)

Responses

Request samples

Content type
application/json
{
  • "contact": {
    }
}

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Get contact

Get an individual contact by ID.

Authorizations:
bearerHttpAuthentication
path Parameters
contact_id
string

The ID of the contact

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Update contact

Update an individual contact by ID.

Authorizations:
bearerHttpAuthentication
path Parameters
contact_id
string

The ID of the contact

Request Body schema: application/json
required
required
object (UpdateContactInput)

Responses

Request samples

Content type
application/json
{
  • "contact": {
    }
}

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Events

List events

Returns a paginated list of events for a venue. Requires a venue_id query parameter.

Results can be filtered by status, event type, source, spaces, contact, or date range, and sorted by a configurable field and direction.

Pagination uses page and per_page query parameters. page defaults to 1; values below 1 are treated as 1. per_page defaults to 25 and is clamped between 1 and 100.

Pagination metadata is returned in data.meta with current_page, per_page, total_count, and total_pages. The paginated event array is returned in data.events.

Authorizations:
bearerHttpAuthentication
query Parameters
venue_id
required
string

The ID of the venue to list events for

status
Array of strings
Items Enum: "lead" "qualified" "proposal_sent" "confirmed" "balance_due" "completed" "lost"

Filter by one or more event statuses

event_type
Array of strings

Filter by one or more event type names configured for the venue

source
Array of strings

Filter by one or more event sources

spaces
Array of strings

Filter by one or more space slugs configured for the venue

contact_id
string

Filter events by contact ID

order
string
Default: "created_at"
Enum: "created_at" "updated_at" "start_time" "name"

Field to sort results by

sort_direction
string
Default: "desc"
Enum: "asc" "desc"

Sort direction

date_from
string <date-time>
Example: date_from=2025-01-01T00:00:00Z

Start of date range filter (ISO 8601). Must be provided together with date_to.

date_to
string <date-time>
Example: date_to=2025-12-31T23:59:59Z

End of date range filter (ISO 8601). Must be provided together with date_from.

date_field
string
Default: "created_at"
Enum: "created_at" "updated_at" "start_time"

Which date field the date_from/date_to range applies to

page
integer >= 1
Default: 1

Page number

per_page
integer [ 1 .. 100 ]
Default: 25

Number of results per page (max 100)

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Create event

Creates a new event in a venue.

Creating an event requires a venue_id. You can get a list of available venues for the authorizing Perfect Venue user by calling the list venues endpoint.

Creating an event requires venue_id, name, and either an event.contact_id or a top-level contact object. start_time and end_time are optional, but recommended for events with a known schedule. When you create a contact via the create contact endpoint, you will get back a unique identifier for the contact that can be used to create events. You can also provide a top-level contact object to create or reuse a contact inline.

Authorizations:
bearerHttpAuthentication
Request Body schema: application/json
required
required
object (CreateEventInput)
object

Inline contact creation. Provide either contact_id inside event or a top-level contact object, not both. If a contact with the given email already exists in the organization, it will be used.

object

An initial message from the contact to the venue, created along with the event

Responses

Request samples

Content type
application/json
Example
{
  • "event": {
    },
  • "message": {
    }
}

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Get event

Get an individual event by ID.

Authorizations:
bearerHttpAuthentication
path Parameters
event_id
string

The ID of the event

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Update event

Update an individual event by ID.

Authorizations:
bearerHttpAuthentication
path Parameters
event_id
string

The ID of the event

Request Body schema: application/json
required
required
object (UpdateEventInput)

Responses

Request samples

Content type
application/json
{
  • "event": {
    }
}

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Venues

List venues

List venues available to the Perfect Venue user who authorized your application. Perfect Venue provisions OAuth application access for partner developers during integration setup, but a Perfect Venue user must log in and grant permission before your integration can call the API.

Authorizations:
bearerHttpAuthentication

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": [
    ]
}

Get venue

Get an individual venue by ID.

Authorizations:
bearerHttpAuthentication
path Parameters
venue_id
string

The ID of the venue

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "data": {
    }
}

Webhooks

Perfect Venue can notify your application in real time when data changes for venues available to the authorizing Perfect Venue user. Use webhooks for incremental updates, and use the REST API to fetch the current resource state whenever you need to reconcile.

Quick start

Use this flow when adding webhook support:

  1. Build an HTTPS endpoint that can receive POST requests from Perfect Venue.
  2. Send Perfect Venue your endpoint URL and the event types your integration needs.
  3. Perfect Venue will add the webhook endpoint and event subscriptions to your OAuth application.
  4. Store the endpoint signing secret provided during setup.
  5. Verify the PV-Signature header on every delivery.
  6. Use PV-Webhook-Id as your idempotency key.
  7. Return a 2xx response only after you have safely accepted the delivery.
  8. Fetch the latest resource from the REST API when your local copy needs reconciliation.

Subscribing to events

Webhook subscriptions are configured by Perfect Venue during integration setup. To subscribe, send eng@perfectvenue.com:

Field Description
Endpoint URL The HTTPS URL where Perfect Venue should send webhook deliveries
Event types One or more public event type names from the table below
OAuth application The OAuth application Perfect Venue configured for your integration

Example setup request:

Endpoint URL: https://example.com/perfect-venue/webhooks
Event types: event.created, event.status_changed, event.deleted, contact.created
OAuth application: Partner Integration Name

Perfect Venue configures webhook subscriptions on your OAuth application. Your endpoint can subscribe to multiple event types.

Your endpoint must be reachable over HTTPS and should be prepared to receive duplicate deliveries.

Your endpoint receives events only for venues available to users who have authorized your OAuth application.

Delivery request

Each delivery is an HTTPS POST to your configured endpoint.

Required headers:

Header Description
Content-Type Always application/json
PV-Webhook-Id Stable delivery ID. Use this to deduplicate retries.
PV-Signature Timestamped HMAC signature used to verify the request.

Example delivery:

POST /perfect-venue/webhooks HTTP/1.1
Content-Type: application/json
PV-Webhook-Id: f8c1a0e2-6d2f-4a0d-99a2-1fd6f9f3c1aa
PV-Signature: t=1718557200,v1=5257a869e7ecebeda32affa62cdca3fa...

Payload envelope

The JSON body always follows this envelope:

{
  "id": "f8c1a0e2-6d2f-4a0d-99a2-1fd6f9f3c1aa",
  "type": "event.created",
  "api_version": "2026-06-01",
  "created_at": "2026-06-16T17:00:00Z",
  "data": {
    "id": "dfa4b137-1b3e-4927-a3d8-0e0e5bd601e3",
    "venue_id": "VENUE_ID",
    "contact_id": "CONTACT_ID",
    "name": "Henry's Wedding",
    "status": "lead",
    "start_date": "2026-08-15T18:00:00Z",
    "end_date": "2026-08-15T23:00:00Z",
    "start_time": "2026-08-15T18:00:00Z",
    "end_time": "2026-08-15T23:00:00Z",
    "event_type": "Wedding",
    "spaces": ["main-hall"],
    "created_at": "2026-06-16T17:00:00Z",
    "updated_at": "2026-06-16T17:00:00Z"
  }
}

The data object contains the payload for the specific event type.

Event types

Type Fires when
event.created An event (booking or lead) is created
event.status_changed An event's status changes; payload includes the previous status
event.deleted An event is deleted (soft deleted)
contact.created A new inbound inquiry arrives

Payloads

event.created and event.deleted

The data payload is the Event object — the same shape returned by Get Event and List Events.

Example event.created payload:

{
  "id": "f8c1a0e2-6d2f-4a0d-99a2-1fd6f9f3c1aa",
  "type": "event.created",
  "api_version": "2026-06-01",
  "created_at": "2026-06-16T17:00:00Z",
  "data": {
    "id": "dfa4b137-1b3e-4927-a3d8-0e0e5bd601e3",
    "venue_id": "VENUE_ID",
    "contact_id": "CONTACT_ID",
    "name": "Henry's Wedding",
    "status": "lead",
    "start_date": "2026-08-15T18:00:00Z",
    "end_date": "2026-08-15T23:00:00Z",
    "start_time": "2026-08-15T18:00:00Z",
    "end_time": "2026-08-15T23:00:00Z",
    "group_size": 120,
    "source": "Partner Integration",
    "campaign": "Summer 2026",
    "amount": 150000,
    "event_type": "Wedding",
    "spaces": ["main-hall"],
    "created_at": "2026-06-16T17:00:00Z",
    "updated_at": "2026-06-16T17:00:00Z"
  }
}

Example event.deleted payload:

{
  "id": "9be6ab48-55f2-46f3-bd07-74af424c0f03",
  "type": "event.deleted",
  "api_version": "2026-06-01",
  "created_at": "2026-06-17T15:30:00Z",
  "data": {
    "id": "dfa4b137-1b3e-4927-a3d8-0e0e5bd601e3",
    "venue_id": "VENUE_ID",
    "contact_id": "CONTACT_ID",
    "name": "Henry's Wedding",
    "status": "lost",
    "start_date": "2026-08-15T18:00:00Z",
    "end_date": "2026-08-15T23:00:00Z",
    "start_time": "2026-08-15T18:00:00Z",
    "end_time": "2026-08-15T23:00:00Z",
    "event_type": "Wedding",
    "spaces": ["main-hall"],
    "created_at": "2026-06-16T17:00:00Z",
    "updated_at": "2026-06-17T15:30:00Z"
  }
}

contact.created

The data payload is the Contact object — the same shape returned by Get Contact.

Only inbound inquiries emit this event.

Example contact.created payload:

{
  "id": "7f6adfe6-9e01-4644-b850-3877b56526c8",
  "type": "contact.created",
  "api_version": "2026-06-01",
  "created_at": "2026-06-16T17:00:00Z",
  "data": {
    "id": "CONTACT_ID",
    "venue_id": "VENUE_ID",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "email": "ada@example.com",
    "phone": "+14155550100"
  }
}

event.status_changed

A thin payload carrying only what changed. Use the id to fetch the full event from the REST API if you need other fields.

Field Type Description
id string Event ID
venue_id string Venue ID
status string New status
previous_status string|null Status before the change
updated_at string ISO 8601 timestamp of the change

Example:

{
  "id": "f8c1a0e2-6d2f-4a0d-99a2-1fd6f9f3c1aa",
  "type": "event.status_changed",
  "api_version": "2026-06-01",
  "created_at": "2026-06-16T17:00:00Z",
  "data": {
    "id": "dfa4b137-1b3e-4927-a3d8-0e0e5bd601e3",
    "venue_id": "VENUE_ID",
    "status": "confirmed",
    "previous_status": "proposal_sent",
    "updated_at": "2026-06-16T17:00:00Z"
  }
}

Signature verification

Every delivery includes a PV-Signature header:

PV-Signature: t=1718557200,v1=5257a869e7ecebeda32affa62cdca3fa...

t is the Unix timestamp the request was signed. v1 is the HMAC-SHA256 hex digest of "#{msg_id}.#{t}.#{raw_body}" where msg_id is the value of the PV-Webhook-Id header, computed with your endpoint's signing secret.

Verification steps:

  1. Read the raw request body before parsing JSON.
  2. Parse t and v1 from PV-Signature.
  3. Reject requests where t is too far from the current time. We recommend a 5-minute tolerance.
  4. Build the signed payload as PV-Webhook-Id + "." + t + "." + raw_body.
  5. Compute the HMAC-SHA256 hex digest with your endpoint signing secret.
  6. Compare the computed digest to v1 using a constant-time comparison.

Including PV-Webhook-Id in the signed content means a replay attack with a swapped webhook ID will fail verification.

Example Node.js verifier:

const crypto = require("crypto");

function verifyPerfectVenueWebhook({ rawBody, webhookId, signatureHeader, signingSecret }) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => part.split("="))
  );
  const timestamp = parts.t;
  const expected = parts.v1;

  const signedPayload = `${webhookId}.${timestamp}.${rawBody}`;
  const actual = crypto
    .createHmac("sha256", signingSecret)
    .update(signedPayload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(actual, "hex"),
    Buffer.from(expected, "hex")
  );
}

Idempotency

The PV-Webhook-Id header (and the id field in the body) is a stable identifier for an event delivery. It does not change across retries. Use it to deduplicate: if you have already processed an id, acknowledge with 2xx and ignore the repeat.

Delivery is at-least-once. Your handler must be safe to run twice for the same PV-Webhook-Id.

Response handling

Return a 2xx status after your endpoint has accepted the delivery. Any non-2xx response, timeout, or connection error is treated as a failed delivery and retried.

Recommended handler behavior:

  1. Verify the signature.
  2. Check whether PV-Webhook-Id has already been processed.
  3. Persist the delivery or enqueue work in your system.
  4. Return 2xx.
  5. Process the event asynchronously when possible.

Ordering and sync

There is no global ordering guarantee. Deliveries are dispatched concurrently and can arrive out of order. Events about the same resource carry a created_at you can order by, and you can always read the current state from the REST API to reconcile. For high-confidence syncs, combine webhooks with periodic GET /events backfills filtered by updated_at.

Retries and endpoint health

Failed deliveries are retried automatically.

If an endpoint fails repeatedly, it may be disabled until it is healthy again.

event.created Webhook

Sent when an event is created for a venue available to a Perfect Venue user who authorized your application.

Authorizations:
bearerHttpAuthentication
Request Body schema: application/json
required
id
required
string

Stable webhook delivery ID. Matches the PV-Webhook-Id header.

type
required
string
Value: "event.created"
api_version
required
string
created_at
required
string <date-time>
required
object

Event object emitted by the webhook. This uses the same serializer as Get Event and List Events.

Responses

Request samples

Content type
application/json
{
  • "id": "f8c1a0e2-6d2f-4a0d-99a2-1fd6f9f3c1aa",
  • "type": "event.created",
  • "api_version": "2026-06-01",
  • "created_at": "2026-06-16T17:00:00Z",
  • "data": {
    }
}

event.status_changed Webhook

Sent when an event status changes. Fetch the event from the REST API if you need the full current event state.

Authorizations:
bearerHttpAuthentication
Request Body schema: application/json
required
id
required
string

Stable webhook delivery ID. Matches the PV-Webhook-Id header.

type
required
string
Value: "event.status_changed"
api_version
required
string
created_at
required
string <date-time>
required
object

Responses

Request samples

Content type
application/json
{
  • "id": "f8c1a0e2-6d2f-4a0d-99a2-1fd6f9f3c1aa",
  • "type": "event.status_changed",
  • "api_version": "2026-06-01",
  • "created_at": "2026-06-16T17:00:00Z",
  • "data": {
    }
}

event.deleted Webhook

Sent when an event is deleted.

Authorizations:
bearerHttpAuthentication
Request Body schema: application/json
required
id
required
string

Stable webhook delivery ID. Matches the PV-Webhook-Id header.

type
required
string
Value: "event.deleted"
api_version
required
string
created_at
required
string <date-time>
required
object

Deleted event object emitted by the webhook. Events are soft deleted, so the payload contains the event's last public shape.

Responses

Request samples

Content type
application/json
{
  • "id": "9be6ab48-55f2-46f3-bd07-74af424c0f03",
  • "type": "event.deleted",
  • "api_version": "2026-06-01",
  • "created_at": "2026-06-17T15:30:00Z",
  • "data": {
    }
}

contact.created Webhook

Sent when a new inbound inquiry creates a contact.

Authorizations:
bearerHttpAuthentication
Request Body schema: application/json
required
id
required
string

Stable webhook delivery ID. Matches the PV-Webhook-Id header.

type
required
string
Value: "contact.created"
api_version
required
string
created_at
required
string <date-time>
required
object

Contact object emitted by the webhook. This uses the same serializer as Get Contact.

Responses

Request samples

Content type
application/json
{
  • "id": "7f6adfe6-9e01-4644-b850-3877b56526c8",
  • "type": "contact.created",
  • "api_version": "2026-06-01",
  • "created_at": "2026-06-16T17:00:00Z",
  • "data": {
    }
}

Changelog

1.3.0 — 2026-06-18

Webhooks

New Webhooks section

Outgoing webhooks are now available. Register an HTTPS endpoint on your OAuth application and subscribe to the event types you care about. Perfect Venue will POST a signed JSON payload to your endpoint whenever a subscribed event occurs.

Event types available in this release:

Type Fires when
event.created An event is created
event.status_changed An event's status changes
event.deleted An event is deleted
contact.created A new inbound inquiry arrives

See the Webhooks section for the full delivery contract, payload schemas, signature verification, and retry behavior.

Events

New List Events endpoint

GET /events is now available. Returns a paginated list of events for a venue with support for filtering by status, event type, source, spaces, contact, and date range.

Parameter Type Description
venue_id string Required. The ID of the venue
status string[] Filter by status: lead, qualified, proposal_sent, confirmed, balance_due, completed, lost
event_type string[] Filter by event type name
source string[] Filter by event source
spaces string[] Filter by space slug
contact_id string Filter by contact ID
order string Sort field: created_at (default), updated_at, start_time, name
sort_direction string asc or desc (default)
date_from string ISO 8601 start of date range (requires date_to)
date_to string ISO 8601 end of date range (requires date_from)
date_field string Date field for range filter: created_at (default), updated_at, start_time
page integer Page number (default: 1)
per_page integer Results per page, max 100 (default: 25)

1.2.0 — 2026-04-01

Venues

New fields on Get Venue and List Venues

Field Type Description
event_types object[] Event types configured for the venue, each with a name
spaces object[] Spaces available at the venue, each with slug, name, standing, and seated capacity
custom_fields object[] Custom fields configured for the venue, each with key, name, data_type, and optional options

Events

New fields on Create Event and Update Event

The following optional fields are now supported:

Field Type Description
amount integer Estimated event value in cents (e.g. 150000 for $1,500.00)
event_type string Event type name, must match a type configured for the venue
spaces string[] Space slugs to assign to the event
custom object Custom field values keyed by field key

Inline contact creation on Create Event

You can now create a contact inline when creating an event by passing a top-level contact object instead of a contact_id. If a contact with the given email already exists in your organization, it will be reused.

Initial message on Create Event

You can now include an initial message from the contact to the venue when creating an event by passing a top-level message object with a text field.

New Update Event endpoint

PATCH /events/{event_id} is now available. All fields are optional — only the fields you include will be updated.