Beyond
Gates

Beyond Gates integration API

Send your registrants to Beyond Gates as they sign up on your platform. They can then print their event badge at our self-service kiosks by scanning the QR code your platform already gave them, and enter through our gates with it.

There are three ways to connect. All of them feed the same pipeline:

MethodWho calls whomDelay to our kiosks
PushYou call our endpoint with an API keyImmediate
WebhookYou call our endpoint, signing each requestImmediate
PullWe call your API on a scheduleUp to 30 seconds

The API speaks JSON over HTTPS. You send your records in your own format: we map your field names once, during setup. People also register on the event day, so whichever method you choose must deliver a registrant before they reach our kiosk.

Base URL
https://api.beyondgates.net

Authentication

Each integration belongs to one event and has its own endpoint. We send you its integration_id with its credentials when we set it up, first for a test event, then for the live one.

  • Push authenticates with an API key starting with bgk_, sent as Authorization: Bearer bgk_… or X-Api-Key: bgk_….
  • Webhooks authenticate with a signature of each request: see Signing requests.
  • Pull uses the token or key of your API, which we store encrypted.
Keep keys and signing secrets on your servers, never in a browser or app. We cannot display a key again; if one leaks, ask us to rotate it and the old one stops working at once.
Authenticated request
curl https://api.beyondgates.net/api/integrations/$INTEGRATION_ID/inbound \
  -H "Authorization: Bearer $BEYOND_GATES_KEY" \
  -H "Content-Type: application/json" \
  -d '[]'

# or, if your client can't set Authorization:
curl https://api.beyondgates.net/api/integrations/$INTEGRATION_ID/inbound \
  -H "X-Api-Key: $BEYOND_GATES_KEY" \
  -H "Content-Type: application/json" \
  -d '[]'

Errors

We answer with standard HTTP status codes. A 200 means the request was processed, but individual registrants can still fail: read failed and errors in the ingest summary.

StatusMeaning
200Processed; see the counts and errors in the summary
400The body is not valid JSON
401Missing or wrong API key or signature, or the integration is paused
403This integration is set up for Pull and accepts no requests
413The request is too large: send the registrants in smaller batches
5xxA temporary problem on our side: retry

Errors other than 200 come with a JSON body whose message says what went wrong.

Error body
{
  "statusCode": 401,
  "message": "Invalid API key",
  "error": "Unauthorized"
}

Retries and idempotency

Every request is safe to repeat. A registrant is identified by your id: sending it again updates the earlier record, or leaves it unchanged, and never creates a duplicate.

  • Retry 5xx responses, timeouts and network errors with a backoff, for example after 10 s, 1 min, 5 min and 30 min.
  • Don't retry 400, 401, 403 or 413 as they are: fix the request first.
  • We apply registrants in the order they arrive. Send the full current record each time, not only the changed fields, so a late retry can't bring back old data.
  • Set your client timeout to 30 seconds.

Limits

LimitValue
Barcode200 characters, unique within the event
Name, email200 characters
Pull interval30 seconds by default, 10 seconds at the shortest

A request is answered when processing ends, typically in under a second. If a request holds more registrants than we process at once, the ingest summary says so in errors: send the rest in a further request.

Registrants

The registrant object

One person registered for the event. Only id and name are required; send the barcode too, because it is what attendees scan. These are the field names to use if you have no format of your own: with them there is nothing to map.

Attributes

idstringRequired
Your unique, stable id for the person in this event. A record with the same id updates the earlier one.
namestringRequired
Full name, printed on the badge. First and last names in separate fields work too.
barcodestringRecommended
The exact text your QR code contains, character for character. Our kiosk, desk and gate scanners read it. Plain text of letters, digits and - _ / . :; if your QR holds a URL, send the whole URL. Missing, we use id.
categorystringRecommended
Your ticket class or attendee type, as a stable code such as VIP. We match each code to one of our ticket types, which sets the badge design. Tell us your codes before go-live.
statusstring
Lets you cancel a registration. The values that mean cancelled, such as cancelled, are agreed during setup. A cancelled attendee can't print a badge or enter.
emailstring
Contact email.
phonestring
Any format. Saudi numbers are normalised to +9665….
languagestring
ar or en; values such as Arabic also work.
extra fieldsany
Anything else you want kept or printed on the badge. We agree the field list during setup.
The registrant object
{
  "id": "98123",
  "name": "Sara Al-Qahtani",
  "email": "sara@example.com",
  "phone": "+966551234567",
  "language": "ar",
  "category": "VIP",
  "barcode": "EXT-2026-98123",
  "status": "confirmed",
  "company": "Acme",
  "job_title": "CTO",
  "updated_at": "2026-10-01T09:30:00Z"
}

Sending your own format

You don't have to rename anything. Send registrants as your platform stores them, nested and named your way, and we map each of our fields to yours once, during setup.

  • Paths reach into nested objects and lists: {person.contact.mail}, {tickets[0].class}.
  • A field can combine several of yours: {first_name} {last_name}.
  • Alternatives take the first with a value: {mobile|phone}.
  • The list can sit anywhere in your body, for example under data.attendees.

To set the mapping up, send us one real record from your platform.

What you send
{
  "data": {
    "attendees": [
      {
        "uid": 98123,
        "person": {
          "first_name": "Sara",
          "last_name": "Al-Qahtani",
          "contact": { "mail": "sara@example.com", "mobile": "0551234567" }
        },
        "ticket": { "class": "VIP" },
        "qr_value": "EXT-2026-98123",
        "state": "active",
        "org": { "name": "Acme" }
      }
    ]
  }
}
How we map it
recordsPath   data.attendees
id            {uid}
name          {person.first_name} {person.last_name}
email         {person.contact.mail}
phone         {person.contact.mobile}
category      {ticket.class}
barcode       {qr_value}
status        {state}            cancelled when: cancelled, refunded
company       {org.name}
Push API

Send registrants

POST/api/integrations/{integration_id}/inbound

Creates or updates registrants. Send one or several per request, whenever they register or change; a nightly full sync is also fine. Returns an ingest summary.

Path parameters

integration_idstringRequired
Your integration's id, sent with your credentials.

Headers

AuthorizationstringRequired
Bearer followed by your API key. X-Api-Key works instead.
Content-TypestringRequired
application/json

Body

registrant | registrant[] | objectjsonRequired
One registrant, an array of them, or a wrapper object with the list inside, such as { "data": { "attendees": [ … ] } }.
POST/api/integrations/{integration_id}/inbound
curl https://api.beyondgates.net/api/integrations/$INTEGRATION_ID/inbound \
  -H "Authorization: Bearer $BEYOND_GATES_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": "98123",
      "name": "Sara Al-Qahtani",
      "category": "VIP",
      "barcode": "EXT-2026-98123",
      "status": "confirmed"
    }
  ]'
Response
{
  "received": 3,
  "created": 1,
  "updated": 1,
  "unchanged": 0,
  "cancelled": 0,
  "failed": 1,
  "errors": [
    {
      "externalId": "98125",
      "reason": "Category \"PRESS\" isn't mapped to a ticket type"
    }
  ]
}
Webhooks

Sending webhooks

POST/api/integrations/{integration_id}/inbound

Your platform posts to our endpoint whenever a registrant is created, updated or cancelled. The endpoint and body shapes are those of Send registrants; instead of an API key, each request carries a signature.

Event names don't matter to us: we read the registrant inside each notification, for example under data. Batches of registrants in one notification work too.

We answer 200 once the registrant is stored, within a second or two. Retry anything else, as described in Retries.

A webhook request
POST /api/integrations/{integration_id}/inbound HTTP/1.1
Host: api.beyondgates.net
Content-Type: application/json
X-Signature: 3f1c0b9e5a…d27e

{
  "event": "registrant.created",
  "data": {
    "id": "98123",
    "name": "Sara Al-Qahtani",
    "category": "VIP",
    "barcode": "EXT-2026-98123",
    "status": "confirmed"
  }
}

Signing requests

Sign every webhook with the signing secret we share with you. The signature is the HMAC-SHA256 of the raw request body, keyed with the secret, in a header:

Headers

X-SignaturestringRequired
The signature as hex or base64. A sha256= prefix is accepted. If your platform already uses another header name, such as X-Hub-Signature-256, we use yours.
Sign the exact bytes you send. Serialising the JSON again after signing, or letting your HTTP client re-encode it, changes the bytes and the request is refused with 401.
Sign and send
BODY='{"event":"registrant.created","data":{"id":"98123","name":"Sara Al-Qahtani"}}'
SIGNATURE=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SIGNING_SECRET" | sed 's/^.* //')

curl https://api.beyondgates.net/api/integrations/$INTEGRATION_ID/inbound \
  -H "Content-Type: application/json" \
  -H "X-Signature: $SIGNATURE" \
  --data-raw "$BODY"

Updates and cancellations

To change a registrant, send the full record again with the same id: a new name, category or barcode replaces the old one.

To cancel one, send it with its cancelled status. Send the registrant, not only an id or an event name, so we can match it. Sending it again later with an active status restores it.

A registrant you delete without telling us stays registered with us, and can still print a badge.

A cancellation
{
  "event": "registrant.cancelled",
  "data": {
    "id": "98123",
    "name": "Sara Al-Qahtani",
    "category": "VIP",
    "barcode": "EXT-2026-98123",
    "status": "cancelled"
  }
}
Pull

How we fetch

If your platform can't call out but already has an API, we call it. Every 30 seconds by default we ask for the registrants changed since our last successful fetch, follow your pagination, and import them as if you had pushed them.

  • The time we send is when our previous successful fetch started, minus 5 seconds, in ISO 8601 UTC.
  • Without a change filter we fetch the whole list each time, which suits events of up to a few thousand registrants.
  • On the event day we can fetch every 10 seconds, if you agree.

Send us your API documentation and a token for a test event to set this up.

Our request to your API
GET /v1/attendees?updated_since=2026-10-01T09:29:55.000Z&cursor=200 HTTP/1.1
Host: api.your-platform.com
Authorization: Bearer <your token>
Accept: application/json
Your response
{
  "data": {
    "attendees": [
      {
        "id": "98123",
        "name": "Sara Al-Qahtani",
        "category": "VIP",
        "barcode": "EXT-2026-98123",
        "status": "confirmed",
        "updated_at": "2026-10-01T09:30:12Z"
      }
    ]
  },
  "meta": { "next_cursor": "400" }
}

Your API requirements

  1. Reachable over HTTPS on a public address, without redirects to another URL.
  2. Authentication in request headers, such as Authorization: Bearer <token> or X-Api-Key.
  3. The event's registrants as JSON, by GET, or POST with a body we fill in. The list can sit anywhere in the response.
  4. A filter by last change taking an ISO 8601 UTC time, such as ?updated_since=…. Recommended.
  5. Cancelled registrants included in those changes, with their cancelled status. A registrant who disappears from your API stays registered with us.
  6. Long lists split into pages in one of the styles under Pagination.
  7. An answer within 20 seconds.

If you restrict access by IP address, ask us for our server's address.

Pagination

We follow your pages until there are no more. Three styles are supported:

StyleHow it works
Page numberWe send page=1, 2, 3… and optionally a page size; we stop at an empty or short page
CursorYour response holds the next cursor; we send it back until it is empty
Next-page URLYour response holds the full URL of the next page; we follow it until there is none

Tell us the parameter names your API uses and where the cursor or next URL sits in the response.

Supported styles
# Page number: we send ?page=1, 2, 3… (and per_page if you name it)
GET /v1/attendees?page=2&per_page=100
→ stops at an empty page, or one shorter than per_page

# Cursor: you return the next cursor, we send it back
GET /v1/attendees?cursor=400
→ stops when the cursor is empty or the page has no records

# Next-page URL: you return the full URL of the next page
{ "data": [ ... ], "links": { "next": "https://api.your-platform.com/v1/attendees?after=400" } }
→ stops when there is no next URL
Objects

The ingest summary

What we did with the registrants in a request. Returned by Push and Webhook requests with a 200.

Attributes

receivedinteger
Registrants found in the request.
createdinteger
New registrants, now printable at our kiosks.
updatedinteger
Registrants whose details changed.
unchangedinteger
Registrants we already had exactly as sent.
cancelledinteger
Registrants cancelled by this request.
failedinteger
Registrants we couldn't store; see errors.
errorsarray of objects
One entry per failed registrant, up to 50.
The ingest summary
{
  "received": 3,
  "created": 1,
  "updated": 1,
  "unchanged": 0,
  "cancelled": 0,
  "failed": 1,
  "errors": [
    {
      "externalId": "98125",
      "reason": "Category \"PRESS\" isn't mapped to a ticket type"
    }
  ]
}
Go live

Testing checklist

We start on a test event with its own credentials, and switch to the live event once a registrant has gone end to end: from your platform to a printed badge.

What we need from you

  • The method you choose: Push, Webhook or Pull
  • One real registrant record, or for Pull your API documentation and a test token
  • Your category codes, and the status values that mean cancelled
  • A sample of what your QR codes contain
  • Expected registrations on the event day and the peak per minute, and a contact for that day

Test steps

  1. Send three registrants, one per category you use. We confirm each arrived as expected.
  2. Change one registrant's name and send it again: it updates and doesn't duplicate.
  3. Cancel one: our kiosk then refuses to print their badge.
  4. Scan one of your QR codes at our kiosk: the badge prints within seconds.
  5. Register someone new on your platform and time how long they take to reach our kiosk.