Developer API & Webhooks Guide (Beta)
BETA: The Ticket Falcon Organizer API allows you to programmatically access your event data, sync attendee lists, and build custom check-in solutions. Our new Webhook system provides real-time notifications whenever a ticket is sold, refunded, or an attendee is checked in.
The Ticket Falcon Organizer API lets you read your event data, sync attendee lists, check people in from your own app, and receive real time notifications when tickets are sold, refunded, or scanned.
The API is in Beta and access is granted per account.
How to enable access
- Sign in to Ticket Falcon.
- Go to My Account > Developer Portal.
- If the page shows a Private Beta Access message, contact Ticket Falcon Support and ask to have API access turned on for your account. Once it is enabled you will get an email and the full portal appears.
The portal has four tabs: Credentials, Permissions, Webhooks, and Documentation.
Managing API keys
Your API key is the password for your event data. Treat it like a credit card number.
- Generating a key. Open the Credentials tab and click Generate New Key. The key is shown one time only. Copy it before you leave the page. If you lose it, generate a new one.
- Using your key. Send it as a header on every request:
X-TF-API-KEY: tf_live_...
- Base URL:
https://www.ticketfalcon.com/wp-json/ticketfalcon/v1/organizer
- Rotation with no downtime. If you think a key is compromised, generate a new one. Your old key keeps working for 24 hours so you have time to update your apps without breaking them.
- Rate limit. 60 requests per minute. Going over returns a
429response.
Team permissions
The Permissions tab controls which of your team members can reach your events with their own API key.
- Team members with the Event Manager role always have access.
- View Only and Check-In Only members do not, until you allow them. Use the Global Role Policies checkboxes to allow every member with that role, or tick Allow Access next to specific people under Specific User Exceptions.
- Access still follows the events each person is assigned to. Allowing someone here never widens which events they can see.
Endpoints
GET /events
Returns the events you own plus events shared with you by other organizers.
| Parameter | Description |
|---|---|
status |
upcoming (default), ended, all, draft, cancelled. past works as an alias for ended. |
search |
Filter by event name, partial match. |
event_id |
One ID, or several separated by commas. |
start_date |
Only events starting on or after this date. |
page |
Page number. Default 1. |
per_page |
Events per page. Default 100, maximum 200. |
Each event returns event_id, name, status, access_role, start_date, end_date, timezone, and event_url.
Paging comes back as headers rather than in the body, so the response stays a plain array: X-TF-Page, X-TF-Per-Page, X-TF-Total, and X-TF-Total-Pages. Status and date filters are applied after paging, so a page can hold fewer rows than per_page. Keep asking for pages until you pass X-TF-Total-Pages rather than stopping at the first short page.
GET /attendees
| Parameter | Description |
|---|---|
event_id |
Optional. Leave it off to return every event your key can reach. |
page |
Page number. 50 attendees per page. Keep paging until you get fewer than 50 back. |
modified_after |
Only return attendees changed since this date, so you sync updates instead of everything. |
Each attendee returns first_name, last_name, email, ticket_type, ticket_number, event_id, checked_in, seat, paddle_no, and updated_at.
checked_in here is readable text, either Not Checked-In or Checked-In at 7:15 PM CST. In webhook payloads the same idea is a true or false value, so do not reuse the same parsing code for both.
Refunded tickets and donation lines are left out.
Sync the cheap way. Every response includes a meta.next_sync_url with the current timestamp already filled in. Save it and use it as your next request, and you only pull what changed.
POST /checkin
Marks an attendee as checked in. The dashboard updates live for everyone else watching, and your webhook fires.
| Body parameter | Type | Description |
|---|---|---|
order_number |
String | Required. The Ticket Falcon order number, for example 10550091. |
ticket_number |
String | Required. The ticket number, for example 9781917. |
status |
Integer | Required. Set to 1. Undoing a check-in through the API is not supported yet. |
Both numbers have to match the same ticket. Sending one without the other returns a 404.
Example response:
{
"success": true,
"order_number": "10550091",
"ticket_number": "9781917",
"is_checked_in": true,
"already_checked_in": false,
"message": "Check-in successful.",
"display_text": "01/01/2026, 9:30 pm CST",
"is_bidder": true,
"paddle_number": "105"
}
- Duplicate prevention. Scanning a ticket that is already checked in returns
already_checked_in: truealong with the original check-in time indisplay_text. Show a duplicate warning rather than a success message. - Real time sync. A check-in through the API updates the web dashboard for every other admin without a page refresh.
- Auction fields.
is_bidderandpaddle_numberapply to auction events. Everything else returnsfalseand an empty string.
Using webhooks
Webhooks push data to you the moment something happens, so you never have to ask "is there anything new?".
- Go to the Webhooks tab.
- Enter your destination URL. That can be your own server, or a webhook URL from Zapier, Make, or n8n if you would rather not host anything.
- Click Save Webhook URL.
Supported events
| Event | Fires when |
|---|---|
order.completed |
An order is confirmed. This covers orders that land as processing as well as completed. |
order.refunded |
A whole order is refunded. |
attendee.updated |
Anything on a ticket changes: attendee name, email, phone, ticket type, seat, paddle number, or a check-in. |
ticket.refunded |
A single ticket inside an order is refunded. |
attendee.updated is worth planning for. Buyers often fill in attendee names after checkout, so expect a follow-up delivery with the final details a while after the sale.
What a payload looks like
{
"event": "order.completed",
"order_id": 10550091,
"status": "completed",
"timestamp": "2026-01-01 21:30:00",
"items": [
{
"item_id": 884512,
"name": "General Admission",
"quantity": 1,
"event_id": "40213",
"ticket_type": "General Admission",
"ticket_number": "9781917",
"attendee_name": "Jane Doe",
"email": "jane@example.com",
"phone": "555-555-5555",
"checked_in": false,
"is_refunded": false
}
]
}
One entry in items is one ticket for standard ticket types. Raffle tickets and add-ons arrive as a single entry with a quantity.
Verify every delivery
Each request carries two headers: X-TF-Event with the event name, and X-TF-Signature, which is an HMAC SHA256 of the raw request body signed with your signing secret. Your secret is on the Webhooks tab, and you can generate a new one there at any time.
Check the signature before you trust a payload:
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', $body, TF_WEBHOOK_SECRET);
if (!hash_equals($expected, $_SERVER['HTTP_X_TF_SIGNATURE'] ?? '')) {
http_response_code(401);
exit;
}
Sign the raw body exactly as it arrives, before any parsing or re-encoding, and compare with a constant time function such as hash_equals().
Generating a new secret takes effect straight away, so update your listener first.
Delivery behavior
Deliveries are sent once and are not retried. For anything financial or compliance related, reconcile on a schedule with GET /attendees?modified_after= so a missed delivery never turns into a missed attendee.
Passing ticket details to another form or tool
A common request is "after someone buys, send the ticket ID into our form", for example a waiver in Jotform. Here are three ways, from most to least automated.
1. Webhook to a prefilled link. Point your webhook at your listener, then build a link per ticket. Most form tools, Jotform included, fill a field from the URL when the parameter matches the field name:
https://form.jotform.com/FORM_ID?ticketId=9781917&orderId=10550091&email=jane@example.com
Email or text that link, or push the data straight into the form tool with their own API. Zapier, Make, and n8n can all do this without any hosting.
2. Poll the attendees endpoint. If you cannot host a listener, call GET /attendees?event_id=123&modified_after=... on a schedule and build the same links from ticket_number, first_name, last_name, and email.
3. Just share a link. Add your form link to Message from Event Organizer in your event settings and it goes out in the ticket confirmation email. This takes a minute and needs no code, but it cannot carry the ticket ID, so ask for the order number on the form instead.
Ticket numbers are eight digits, so treat anything typed in by hand as unverified. Check it against GET /attendees for that event before you rely on it.
Testing safely
Send this header with any request to run in sandbox mode:
X-TF-TEST-MODE: true
Reads behave normally and are tagged as sandbox in the response. A POST /checkin returns what a real check-in would return without actually checking anyone in, so you can test your scanner against live data without touching your guest list.
Troubleshooting
| Response | What it means |
|---|---|
401 API Key missing |
The X-TF-API-KEY header was not sent. |
403 API Access disabled |
API access is not enabled for that account yet. Contact Support. |
403 Invalid or Expired API Key |
The key is wrong, or it is an old key past the 24 hour rotation window. |
403 No access to this event |
The key owner is not on that event, or their team role has not been allowed in the Permissions tab. |
404 Ticket not found |
The order number and ticket number do not match the same ticket, or the order is not paid. |
429 Rate limit exceeded |
More than 60 requests in a minute. Slow down and use modified_after to pull less. |
Webhook not arriving? Check that API access is enabled, that a URL is saved on the Webhooks tab, and that your endpoint answers on HTTPS with a valid certificate.
Still stuck? Contact Ticket Falcon Support and tell us the account email and roughly when the request was made. We keep 7 days of API request logs. Every error is recorded, and successful calls are sampled once a minute per endpoint, so we can see what your key was doing even if not every single call is listed.