# MRSHL

Use the MRSHL integration to export Eventication visitor and member data (lists, members, check-in status, wristband codes) to [MRSHL](https://www.mrshl.be) for F\&B operations.

### Setup in Eventication

Go to: `Admin → Event → General → Integrations → + (button) → MRSHL`. If you can't find it, ask an Eventication admin to enable the MRSHL module for your event.

{% hint style="info" %}
A MRSHL API key is scoped to an event. Each event needs its own MRSHL integration.
{% endhint %}

### Configuring access

After creating the integration, click the **edit** button to configure which data MRSHL can access:

* **Memberships**: toggle on/off to allow access to the members endpoint.
* **Lists**: select which lists MRSHL can access. Only selected lists will be returned by the lists endpoint. If no lists are selected, the lists endpoint will return `403 Access denied`.

{% hint style="warning" %}
Review your access settings carefully. Only grant access to lists and members that MRSHL needs for their F\&B operations.
{% endhint %}

### API key / authentication

You'll see an **API key** in the MRSHL integration screen. Keep this key **secret** (treat it like a password):

* Do not share it
* Do not commit it to git
* Do not paste it in screenshots / tickets
* Store it in your secret manager or encrypted environment variables

Authenticate every request with the Authorization Bearer header:

```http
Authorization: Bearer YOUR_MRSHL_API_KEY
```

### Base URL

All endpoints are hosted on the API domain:

* `https://api.eventication.com`

Example:

* `https://api.eventication.com/mrshl/lists`

### Pagination

List endpoints are paginated to keep responses fast.

* Query param: `page` (1-based)
* Fixed page size: **100 objects per request**
* The response includes `meta`:
  * `page`, `per_page`, `total_pages`, `total_count`, `next_page`, `prev_page`

Example:

* `GET https://api.eventication.com/mrshl/lists?page=2`

### Rate limiting

Requests are rate-limited:

* **10** requests per minute
* On limit: [HTTP 429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429) with `{ "error": "Rate limit exceeded" }`

***

### Endpoints

#### <mark style="color:$success;">GET</mark> `/mrshl/lists`

Returns list visitors across all accessible lists for the event, including check-in status and wristband codes.

* URL: `https://api.eventication.com/mrshl/lists?page=1`
* Access: requires at least one list to be selected in the integration settings.
* Fields per item:
  * `list_id` — encoded list identifier
  * `list_name` — name of the list
  * `list_entry_id` — encoded list entry identifier
  * `list_entry_name` — name of the responsible person on the list entry
  * `visitor_id` — encoded visitor (list entry item) identifier
  * `visitor_firstname` — visitor's first name
  * `visitor_lastname` — visitor's last name
  * `visitor_email` — visitor's email address
  * `last_checkin_at` — last check-in timestamp (ISO 8601), or `null`
  * `last_checkout_at` — last check-out timestamp (ISO 8601), or `null`
  * `valid_wristband_codes` — array of active wristband codes for this visitor

Response shape:

```json
{
  "data": [
    {
      "list_id": "Z2lkOi8vZXZlbnRpY2F0aW9uL0xpc3QvODYy",
      "list_name": "VIP Guests",
      "list_entry_id": "Z2lkOi8vZXZlbnRpY2F0aW9uL0xpc3RFbnRyeS83NTI",
      "list_entry_name": "John Doe",
      "visitor_id": "Z2lkOi8vZXZlbnRpY2F0aW9uL0xpc3RFbnRyeUl0ZW0vMjIw",
      "visitor_firstname": "John",
      "visitor_lastname": "Doe",
      "visitor_email": "john.doe@example.com",
      "last_checkin_at": "2026-03-15T14:30:00+01:00",
      "last_checkout_at": null,
      "valid_wristband_codes": [
        "igJYuGHKbUgdbmX6UDFD",
        "CxfxKq0THi7OAx1oda0x"
      ]
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 100,
    "total_pages": 3,
    "total_count": 296,
    "next_page": 2,
    "prev_page": null
  }
}
```

#### <mark style="color:$success;">GET</mark> `/mrshl/memberships`

Returns members for the event, including check-in status and wristband codes.

* URL: `https://api.eventication.com/mrshl/memberships?page=1`
* Access: requires the memberships toggle to be enabled in the integration settings.
* Fields per item:
  * `visitor_id` — encoded membership identifier
  * `visitor_firstname` — member's first name
  * `visitor_lastname` — member's last name
  * `visitor_email` — member's email address
  * `last_checkin_at` — last check-in timestamp (ISO 8601), or `null`
  * `last_checkout_at` — last check-out timestamp (ISO 8601), or `null`
  * `valid_wristband_codes` — array of active wristband codes for this member

Response shape:

```json
{
  "data": [
    {
      "visitor_id": "Z2lkOi8vZXZlbnRpY2F0aW9uL01lbWJlcnNoaXAvMjIw",
      "visitor_firstname": "Jane",
      "visitor_lastname": "Smith",
      "visitor_email": "jane.smith@example.com",
      "last_checkin_at": "2026-03-15T12:00:00+01:00",
      "last_checkout_at": "2026-03-15T18:30:00+01:00",
      "valid_wristband_codes": [
        "7RqazTG6pq3RqTIZME3P",
        "eFpmkZbqX1ZHofUzHMSX",
        "O7VgaD2YFOkBtoPIJplC"
      ]
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 100,
    "total_pages": 1,
    "total_count": 42,
    "next_page": null,
    "prev_page": null
  }
}
```

***

### Errors & status codes

[401 Unauthorized](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/401)

* Missing/invalid API key or integration disabled
* Example: `{ "error": "Integration not found. Request UUID: ..." }`

[403 Forbidden](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403)

* Endpoint access not enabled for this integration
* Example: `{ "error": "Access denied" }`

[429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429)

* Rate limit exceeded
* Example: `{ "error": "Rate limit exceeded" }`

***

### Example clients

#### cURL

```bash
curl -sS "https://api.eventication.com/mrshl/lists?page=1" \
  -H "Authorization: Bearer YOUR_MRSHL_API_KEY" \
  -H "Accept: application/json"
```

#### Ruby (Net::HTTP)

```ruby
require 'json'
require 'net/http'
require 'uri'

API_KEY = 'YOUR_MRSHL_API_KEY'
BASE_URL = 'https://api.eventication.com'

def get_json(path, page:)
  uri = URI("#{BASE_URL}#{path}?page=#{page}")
  req = Net::HTTP::Get.new(uri)
  req['Authorization'] = "Bearer #{API_KEY}"
  req['Accept'] = 'application/json'

  res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
  raise "HTTP #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)

  JSON.parse(res.body)
end

# Fetch all visitors across accessible lists (paginated)
page = 1
loop do
  payload = get_json('/mrshl/lists', page: page)
  payload.fetch('data').each do |v|
    puts "#{v['visitor_firstname']} #{v['visitor_lastname']} <#{v['visitor_email']}> — #{v['valid_wristband_codes'].size} wristband(s)"
  end

  next_page = payload.dig('meta', 'next_page')
  break if next_page.nil?
  page = next_page
end
```

#### JavaScript (fetch)

```javascript
const apiKey = 'YOUR_MRSHL_API_KEY';
const baseUrl = 'https://api.eventication.com';

async function fetchPage(path, page) {
  const res = await fetch(`${baseUrl}${path}?page=${page}`, {
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Accept': 'application/json',
    },
  });

  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  return await res.json();
}

// Fetch all memberships
let page = 1;
while (true) {
  const payload = await fetchPage('/mrshl/memberships', page);
  for (const m of payload.data) {
    console.log(m.visitor_firstname, m.visitor_lastname, m.valid_wristband_codes);
  }
  if (!payload.meta.next_page) break;
  page = payload.meta.next_page;
}
```

#### Python (requests)

```python
import requests

api_key = "YOUR_MRSHL_API_KEY"
base_url = "https://api.eventication.com"

page = 1
while True:
    r = requests.get(
        f"{base_url}/mrshl/lists",
        params={"page": page},
        headers={"Authorization": f"Bearer {api_key}", "Accept": "application/json"},
        timeout=30,
    )
    r.raise_for_status()
    payload = r.json()

    for row in payload["data"]:
        codes = ", ".join(row["valid_wristband_codes"])
        print(f"{row['visitor_firstname']} {row['visitor_lastname']} — wristbands: {codes}")

    if payload["meta"]["next_page"] is None:
        break
    page = payload["meta"]["next_page"]
```

{% hint style="info" %}
This API is a work in progress BETA and might change over time.
{% endhint %}
