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:
  1. 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\"}"
  2. 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"
  3. 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. To clear it, send null."
    ]
}
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.
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 The record changed during the write (error_code 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_codeStatusMeaning
invalid_request400A parameter is missing or has an invalid value (error_params names it when the API can tell).
malformed_json400The request body is not valid JSON.
not_allowed400The 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_reused400The Idempotency-Key was already used with a different request. Use a new key for a new request.
missing_api_key401No api_key was sent.
invalid_api_key401The api_key is not valid.
api_access_disabled401API access is not enabled for the workspace.
not_found404No record with that id exists in your workspace.
unknown_endpoint404The URL or HTTP method is not part of the API.
idempotency_key_in_use409A request with the same Idempotency-Key is still being processed. Retry after a moment.
idempotency_key_indeterminate409A 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_changed409The record changed while it was being saved. Retrieve its current values and retry; this request was rolled back.
rate_limited429Too many requests. Wait the number of seconds in the Retry-After header, then retry.
internal_error500An 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). Keep the same 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}"
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