Skip to content
Browse developer documentation

Developer API · Intakes

Intakes on your own site

Leads plan an intake with your gym themselves and confirm it by email. Put the form on your site as a widget or an iframe, or build your own form on the intake API.

Last reviewed 13 October 2026

Overview

The gym turns on online planning under Settings → Intakes → On your website. There it also finds its public code (like jouw-gym-a3f9c1), the code for the widget and iframe, and its API keys.

Whatever way a lead plans, the time is held for 30 minutes and the lead gets an email to confirm. Only then the intake is booked, the lead appears under Leads and the staff member gets a calendar invite.

  • Public page: https://app.rxapp.io/intake/{code} (a link or QR code)
  • Widget: a form on your own page that takes on your site's font
  • Iframe: the public page inside your site, adjusting its height
  • API: your own form, from your own server, with an API key

Widget

Put this where the form should appear. The gym's house style (colour, corners, background) comes along automatically.

<div data-rxapp-intake="jouw-gym-a3f9c1"></div>
<script src="https://app.rxapp.io/intake/v1.js" async></script>

Deviate per page with these attributes on the div:

AttributeMeaning
data-modepopup: a button that opens the form as a pop-up. Default: in the page.
data-colorAccent colour as #rrggbb. Text on it becomes black or white by itself.
data-radiussquare, rounded or pill.
data-fieldsoutlined or filled.
data-backgroundlight, dark or transparent.
data-fontsite (your site's font, default) or system.
data-button-textText on the button, at most 40 characters.
data-languagenl or en. Default: the gym's language.

As a button with a pop-up:

<div data-rxapp-intake="jouw-gym-a3f9c1" data-mode="popup" data-button-text="Plan je intake"></div>
<script src="https://app.rxapp.io/intake/v1.js" async></script>

The div sends events: rxapp:intake-requested when a lead requests an intake, rxapp:intake-error when something goes wrong. For example for your own statistics:

document.addEventListener("rxapp:intake-requested", (event) => {
  // event.detail.starts_at: the chosen time (UTC ISO)
  window.dataLayer?.push({ event: "intake_requested", starts_at: event.detail.starts_at });
});

Iframe

The public page inside your site. The script below sets the height of the iframe to the height of the form.

<iframe src="https://app.rxapp.io/intake/jouw-gym-a3f9c1?embed=1" title="Plan your intake"
        style="width:100%;border:0;min-height:560px" loading="lazy"></iframe>
<script>
  window.addEventListener("message", function (event) {
    if (event.origin !== "https://app.rxapp.io" || !event.data || event.data.type !== "rxapp:intake-height") return;
    document.querySelectorAll('iframe[src^="https://app.rxapp.io/intake/jouw-gym-a3f9c1"]').forEach(function (frame) {
      frame.style.height = event.data.height + "px";
    });
  });
</script>

API

For a form of your own. The API key determines the gym; you never send a gym ID. Base URL: https://api.rxapp.io/api/v1/intake-api.

The gym creates keys under Settings → Intakes → API. A key is shown once. Send it as a Bearer token from your server.

Authorization: Bearer rxk_...
MethodPathPurpose
GET/api/v1/intake-apiThe gym: name, address, time zone, house style, privacy policy, duration and whether online planning is available.
GET/api/v1/intake-api/slots?from&toFree start times per date (UTC), as leads see them. At most 31 days.
POST/api/v1/intake-api/requestsRequest an intake for a lead. 202: the time is held and the lead gets an email to confirm.
GET/api/v1/intake-api/requests/{id}The status of a request: requested, confirmed, expired or cancelled.

Free times for a week:

curl "https://api.rxapp.io/api/v1/intake-api/slots?from=2026-10-20&to=2026-10-26" \
  -H "Authorization: Bearer $RXAPP_INTAKE_API_KEY"
{
  "items": [
    { "date": "2026-10-20", "slots": ["2026-10-20T07:00:00.000Z", "2026-10-20T07:30:00.000Z"] },
    { "date": "2026-10-21", "slots": [] }
  ]
}

Request an intake (Node.js):

const response = await fetch("https://api.rxapp.io/api/v1/intake-api/requests", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RXAPP_INTAKE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    starts_at: "2026-10-20T07:00:00.000Z",
    name: "Sanne Smit",
    email: "sanne@example.com",
    phone: "06 1234 5678",
    consent: true, // the lead agreed to the gym's privacy policy
    language: "nl",
    utm: { source: "website" },
  }),
});
// 202 { "id": "…", "status": "requested", "expires_at": "…" }

Fields of a request:

FieldMeaning
starts_atOne of the free times, exactly as the slots return it.
nameThe lead's name.
emailWhere the confirmation email goes.
phoneRequired; the gym can call the lead.
consenttrue: the lead agreed to the gym's privacy policy (show the link in your form).
languageOptional: nl or en for the emails.
utmOptional: source, medium, campaign, term, content.

Check a request:

curl "https://api.rxapp.io/api/v1/intake-api/requests/0d4e690e-284b-4ca2-82bc-b75e7501778c" \
  -H "Authorization: Bearer $RXAPP_INTAKE_API_KEY"

From request to intake

  1. Fetch the free times (slots). The gym's rules apply: minimum notice, how far ahead, absence of staff.
  2. Send a request with a time and the lead's details. The time is held for 30 minutes.
  3. The lead gets an email in the gym's name with a button to confirm.
  4. After confirming, the intake is booked: the lead appears under Leads (or the existing lead with that email is used), the lead and the staff member get a calendar invite, and reminders are planned.
  5. Not confirmed within 30 minutes? The request expires and the time is free again.

Errors and limits

Errors are JSON with a message and, where useful, a code.

StatusWhat to do
400Check the fields (message says which one), e.g. an invalid email or phone number.
401INTAKE_API_KEY_INVALID: the key is missing, wrong or revoked.
403INTAKE_ORIGIN_NOT_ALLOWED: this site is not one of the gym's allowed domains (widget).
404Unknown gym code or request.
409INTAKE_SLOT_UNAVAILABLE (time just taken: fetch the times again), INTAKE_REQUEST_LIMIT, INTAKE_ONLINE_DISABLED or INTAKE_CHANNEL_DISABLED.
429Too many requests; wait and try again.

Limits: the widget and page allow 10 requests per 15 minutes per IP address, at most 3 requests per email address per day and 100 per gym per hour. The API allows 600 calls per 15 minutes per key.

Security

  • Never put an API key in a website, app or source control; only on your server. Revoke a key at once if it leaks.
  • The widget and iframe need no key: the public code identifies the gym, and the allowed domains decide where they may run.
  • The allowed domains keep others from putting the gym's form on their site. They do not stop spam; the confirmation by email does.
  • A hidden field and a minimum time to fill in the form catch simple bots; such requests get the same answer, but nothing happens.