Introduction
Use the API to read, create, update and delete supported ExhibitDay records from your internal systems or other software. You will need a working knowledge of REST APIs.
Prerequisite
An API key is required for the data endpoints. Find your workspace's key in ExhibitDay (under Workspace Settings > API & Integrations > API Access). API access must be enabled for the workspace.
Methods and Parameters
The API accepts parameters in a JSON request body, request headers or the query string:
-
Parameters in the request body (recommended)
Example: change the name of event 12345 to "Foo Bar".
curl -X PATCH https://api.exhibitday.com/v1/events -H "Content-Type: application/json" -d "{\"api_key\":\"YOUR_API_KEY\", \"id\":\"12345\", \"name\":\"Foo Bar\"}" -
Parameters in request headers
Example: delete event 12345.
curl -X DELETE https://api.exhibitday.com/v1/events -H "api_key: YOUR_API_KEY" -H "id: 12345"
-
Parameters in the query string
(useful for GET filters; send the API key in a header when possible)
Example: retrieve events.
curl -X GET "https://api.exhibitday.com/v1/events?api_key=YOUR_API_KEY"
You can combine these locations. For example, send api_key in a header and update parameters in the JSON body. Send each parameter in one location to avoid ambiguity: when the same parameter appears in more than one location, the API reads its header value first.
Documented parameter values are text, numbers, true/false or null. Arrays and objects are rejected with 400 (error_code invalid_request), with two supported array forms: event_tags accepts tag names, and amount_cost_center_breakdown accepts the allocation array described by that parameter. A request body can be at most 10 MB; JSON bodies also have a limit of 1,000 values.
Updating and Clearing Values
When updating a record, send only the parameters you intend to change. Omitted fields are normally preserved, but documented dependent-field rules still apply. For example, moving a task to another event resets its section unless a valid section for the new event is supplied, reversed event dates are swapped, and converting an expense to a credit (or back) clears the previous type's payment details.
- To change a value, pass in the new value.
- To clear a field that supports clearing, pass in null (a JSON null, or the text "null"). Clearing may empty text, remove an optional value, or reset a field to its documented default. Fields that do not support clearing return an error. See each field's rules.
- An empty value doesn't change anything. If you pass in an empty value (e.g., ""), the current value is kept and the response includes a warnings property naming that parameter. This prevents blank form fields from directly clearing saved values; dependent-field normalization can still apply. The one exception is a task comment's text: a comment cannot be blank, so an empty comment is rejected with an error. Optionally, you can pass in $ignore_params_with_empty_values=true with an update: then every parameter with an empty value or a JSON null is left out of the update (and gets no warning), so with this option the only way to clear a value is the text "null".
Example: remove the due date of task 12345; the empty details value leaves its details unchanged.
curl -X PATCH https://api.exhibitday.com/v1/tasks -H "Content-Type: application/json" -d "{\"api_key\":\"YOUR_API_KEY\", \"id\":\"12345\", \"due_date\":null, \"details\":\"\"}"
Response:
{
"status_code": 200,
"status": "OK",
"message": "Task Update Successful. Task properties that were changed: due_date",
"errors": [],
"warnings": [
"details was empty, so it was left unchanged."
]
}
Note: when you add an object (e.g., with a POST call), passing in null is the same as leaving the parameter out.
Empty Values in Responses
When a value is not set, the API returns it as follows:
- Text is an empty string (""). For example, an event's venue_name and event_notes, a task's details, and the integration_metadata_field_1 to integration_metadata_field_5 fields.
- Dates are null. For example, a task's due_date and task_completed_timestamp, and a miscellaneous expense/credit's payment_due_date and next_partial_payment_due_date.
- Related objects and their ids are null. For example, a task's assignee and event_task_section_id, the event of a general task, and the cost_center_id of an unassigned charge.
- Numbers are 0. For example, an event's budget and ROI values, and a miscellaneous expense/credit's amount.
- Lists are empty lists ([]). For example, an event's tags.
A few fields don't follow these rules (they stay as they are, so existing integrations keep working):
- An event's booth_date_reserved and booth_reserve_by_date are "" when not set.
- In the records returned by /v1/financials/event_costs, payment_due_date, next_partial_payment_due_date and payment_notes are "" when not set, and cost_subtype is null when the cost has no subtype.
- A miscellaneous expense/credit's payment_notes is null when empty (its notes is "").
- In the shorter tasks returned with $return_task_lites, assignee_user_id and task_completed_by_user_id are "" when not set.
Unknown or unsupported parameter names are ignored with warnings, including create fields that must instead be set through PATCH. Known options that are ignored or defaulted produce warnings explaining the actual behavior. Invalid values that would be stored are refused with HTTP 400; a successful warning does not mean the invalid value was saved. Warning messages are deduplicated.
Object responses include the full warnings array. All successful representations also expose X-API-Warnings as a JSON-array header, so raw arrays and bodyless 304 responses retain their normal shape. The header is ASCII-escaped and limited to 6000 characters; X-API-Warnings-Count gives the full count, and X-API-Warnings-Truncated: true means only a prefix fit. Request the default object format when you need the complete warnings array.
Send the documented HTTP verb directly. X-HTTP-Method-Override and form/query _method tunneling are refused with HTTP 400 after normal API-key checks; they cannot silently turn a POST into an update or delete.
For the unset response values described above, handle both null and "". When sending an update, keep their different meanings: null clears a supported field, while an empty string leaves it unchanged.
Response Status Codes
| Code | Status | Description |
| 200 | OK | Indicates that the request was successfully processed. |
| 304 | Not Modified | Only for a GET that sends an If-None-Match header with the ETag of an earlier response from the same URL: the data has not changed since, so the response has no body and you can keep using your saved copy. A request without If-None-Match never gets 304 (see Retrying Safely and Conditional Requests below). |
| 400 | Bad Request | Indicates an issue with the request (i.e., missing required parameter(s) or invalid parameter values), or a change that is not allowed for that event in ExhibitDay (e.g., its tasks or its deletion are locked, or its Misc. Expenses & Credits tab is hidden). |
| 401 | Unauthorized | The api_key is missing or not valid, or API access is not enabled for your workspace (error_code missing_api_key, invalid_api_key or api_access_disabled). |
| 404 | Not Found | The requested object was not found (e.g., an incorrect event id was passed in when retrieving an event). |
| 409 | Conflict | Event deletion could not acquire its inventory lock (error_code inventory_busy: wait briefly, retrieve the event and retry deletion if still needed), required reservation attachments could not be copied (attachment_preservation_failed: the event and inventory were not changed; check the attachments in ExhibitDay before retrying), the record changed during the write (record_changed: the write was rolled back; retrieve the current record and retry), a request with the same Idempotency-Key is still being processed (idempotency_key_in_use: retry after a moment with the same key), or an earlier request with that key did not finish (idempotency_key_indeterminate: check whether it succeeded, then retry with a new key). See Retrying Safely and Conditional Requests below. |
| 429 | Too Many Requests | By default, a workspace has a maximum of 50 calls per second. Responses that reach the enabled workspace's rate check include X-RateLimit-Limit (the effective limit), X-RateLimit-Remaining (calls left in the current window) and X-RateLimit-Reset (seconds until that window resets). A separate IP rate check may refuse a request before these workspace headers are added. A 429 response includes Retry-After with the number of seconds to wait. |
| 500 | Unexpected Internal Error | Indicates that an unexpected error has occurred. |
Error Codes
API-generated error responses have an error_code (a stable code your integration can check instead of reading the message), error_params (the parameters the errors refer to; [] when the API cannot tell) and a request_id. The API also sends the request id in X-Request-Id; include it when contacting support. New codes may be added, so handle a code you don't know by its HTTP status.
{
"status_code": 400,
"status": "Bad Request",
"message": "Error retrieving event(s)",
"errors": [
"Invalid value for the event_id parameter: abc. The event_id parameter must be the numeric id of an event."
],
"error_code": "invalid_request",
"error_params": [
"event_id"
],
"request_id": "3f2c9a7e4b1d4c8e9f0a1b2c3d4e5f60"
}
| error_code | Status | Meaning |
|---|---|---|
| invalid_request | 400 | A parameter is missing or has an invalid value (error_params names it when the API can tell). |
| malformed_json | 400 | The request body is not valid JSON. |
| not_allowed | 400 | The change is not allowed for that record in ExhibitDay (e.g., the event's tasks or its deletion are locked, or its Misc. Expenses & Credits tab is hidden). |
| idempotency_key_reused | 400 | The Idempotency-Key was already used with a different request. Use a new key for a new request. |
| missing_api_key | 401 | No api_key was sent. |
| invalid_api_key | 401 | The api_key is not valid. |
| api_access_disabled | 401 | API access is not enabled for the workspace. |
| not_found | 404 | No record with that id exists in your workspace. |
| unknown_endpoint | 404 | The URL or HTTP method is not part of the API. |
| idempotency_key_in_use | 409 | A request with the same Idempotency-Key is still being processed. Retry after a moment. |
| idempotency_key_indeterminate | 409 | A previous request with this Idempotency-Key did not finish and its outcome is unknown, so this retry was not run (to avoid creating a duplicate). Check whether the earlier request succeeded; to try again, use a new Idempotency-Key. |
| record_changed | 409 | The record changed while it was being saved. Retrieve its current values and retry; this request was rolled back. |
| inventory_busy | 409 | Another inventory operation is still finishing. This request did not start its event changes; wait briefly, retrieve the event's current state and retry deletion if still needed. |
| attachment_preservation_failed | 409 | Required reservation attachment copies could not be created. The event and its inventory were not changed. Check the reservation attachments in ExhibitDay before retrying deletion. |
| rate_limited | 429 | Too many requests. Wait the number of seconds in the Retry-After header, then retry. |
| internal_error | 500 | An unexpected error occurred. Quote the request_id when you contact support. |
Paging and Changed Records
A list returns every matching record unless you ask for a page. On Retrieve Events, Retrieve Tasks, task comments, miscellaneous expenses/credits, event costs and annual expense costs, pass $limit (the most records to return) and $offset (how many to skip). In the response, records_found is the number of records in it, total_records counts every match and has_more is true while more pages follow; the X-Total-Count header carries the total too. In a URL, write $ as %24.
curl "https://api.exhibitday.com/v1/events?%24limit=100&%24offset=0" -H "api_key: YOUR_API_KEY"
To get only what changed, pass filter_by_last_update_timestamp_greater_than_or_equal_to with the latest last-update timestamp you received (on events, tasks, task comments and miscellaneous expenses/credits). Sync Events and Tasks to Another System shows the whole loop.
Retrying Safely and Conditional Requests
Send an Idempotency-Key header (for example a UUID) with a create (POST). Keys are case-sensitive; reuse the exact bytes of the key, URL, body and parameter headers for each retry of that logical create. Once a successful response is stored, an identical retry within 24 hours returns it with Idempotent-Replayed: true instead of creating another record. Reusing a key for a different request is refused with error_code idempotency_key_reused, and a retry while the first request is still running gets 409 (idempotency_key_in_use). If an earlier request stopped before it finished and its outcome is unknown, a retry gets 409 (idempotency_key_indeterminate) instead of risking a duplicate — check whether that request succeeded, then retry with a new Idempotency-Key.
curl -X POST https://api.exhibitday.com/v1/tasks -H "api_key: YOUR_API_KEY" -H "Idempotency-Key: 5f0c2a8e-7d1b-4c3a-9e6f-2b8d4a1c7e90" -H "Content-Type: application/json" -d "{\"name\":\"Order booth carpet\",\"event_id\":12345}"
DELETE does not use Idempotency-Key. If a delete response is lost or uncertain, retrieve the record before retrying. A later 404 not_found means the record is already absent; an unknown-endpoint 404 still means the URL or method needs correction.
Successful GET responses from the data endpoints have an ETag header. Send it back in an If-None-Match header: while the data is unchanged, the API answers 304 Not Modified without a body.
Guides
- Sync Events and Tasks to Another System: Sync event and task changes with paging and timestamp cursors. Upsert records by ID and reconcile deletions separately.
- Export Event Costs to Excel or a BI Tool: Pull every cost line for a year from /v1/financials/event_costs and save it as a CSV file for Excel, Power BI or Google Sheets.
- Create Tasks from Another System: Add tasks to an event from another tool, optionally choose a section or assignee, and retry safely with an Idempotency-Key.
- Read and Update Event Custom Fields: Find each custom field's field_name, read its value, and update custom text, dropdown and URL fields with PATCH /v1/events.
- Handle Errors, Rate Limits and Retries: Use error_code and error_params, share request IDs with support, respect Retry-After, and retry creates safely with an Idempotency-Key.