Developers

Guest name and email on every invoice: what's new in the Onfire Platform API

October 28, 2026 · Onfire Health

Until now, an app connected to an Onfire Health practice could see guest details only on the invoices and Checkout Sessions it created itself. Invoices a practitioner made in the Onfire portal, or that another platform created, were either invisible or stripped of the guest. Many of you told us you needed the guest's name and email address to reconcile invoices with your other systems.

From October 28, 2026, that changes. Once a practice accepts the updated Partner Platform Terms of Service (version October 23, 2026), every app it has connected can:

  • read every invoice of the practice with the guest's name and email, whichever app created it, or the portal;
  • receive webhooks for the practice's invoices and Checkout Sessions, including ones other apps created, with a new client block carrying the guest.

This post explains what changes, when, and what you may need to do. The Onfire Platform Integration Guide has been revised to match, and is the full reference.

When it applies: practice by practice, then everyone

The change is switched on per practice:

  • When a practice accepts the Terms. A practitioner accepts them in the Onfire partner portal, under Settings → Company, or when they're connecting an app. From that moment, everything below applies to that practice's records.
  • On November 22, 2026 (midnight Pacific), the Terms bind every practice, accepted or not.
  • Until a practice accepts, everything you receive for it stays exactly as it is today.

Your app can't accept on a practice's behalf. A platform serving many practices will have both kinds at once for a few weeks, so build for both, and decide per practice and per record from what Onfire sends you. Don't use a global switch.

Practice hasn't acceptedPractice has accepted (every practice from Nov 22, 2026)
Invoices your app created (default reads)Whole record, guest includedUnchanged
?scope=all403Every invoice of the practice, with guest name and email
Checkout Session readclient.name, client.email; client.phone only if your app created itUnchanged
Webhooks for records your app createdNo client keyAdds client: the whole guest, as you supplied it
Webhooks for records your app didn't createNoneEvery invoice.* and checkout_session.* event, with client holding name and email

Reading every invoice: ?scope=all

Both invoice reads (Guide §6.3) accept ?scope=all:

GET /api/v1/core/partner/invoices/?scope=all
GET /api/v1/core/partner/invoices/{invoice_id}?scope=all

The first lists every invoice of the practice. The second retrieves any one of them by its invoice_id. Once the practice has accepted the Terms, ?scope=all needs only a connection the practice hasn't revoked. Before then, it's a 403. Without ?scope=all, you get only the invoices your app created, with full guest details, as today.

Every invoice in a ?scope=all response carries is_own:

  • is_own: true: your app created it. The record is whole.
  • is_own: false: another app or the portal created it. It carries client_name and client_email. client_phone, client_billing_address, member_id, invoice_public_id and pay_url are still present but null, and so are the Checkout Session summary's public_id, success_url and cancel_url. Identify these invoices by invoice_id.
{
  "invoices": [
    {
      "invoice_id": "00000000-0000-4000-8000-0000000000a1",
      "invoice_public_id": null,
      "is_own": false,
      "status": "paid",
      "amount": "2050.00",
      "currency": "USD",
      "client_name": "Example Guest",
      "client_email": "guest@example.com",
      "client_phone": null,
      "client_billing_address": null,
      "member_id": null,
      "pay_url": null,
      "external_invoice_ref": "their-invoice-ref-0001",
      "rate_card_ref": "rc_fixture01",
      "created_at": "2026-06-29T17:50:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 100
}

Two things to watch:

  • Reconcile your own invoices with the default scope. Under ?scope=all, an external_invoice_ref filter can also match another app's invoice that happens to use the same reference. References are unique only within your app.
  • A 403 on ?scope=all for a practice that hasn't accepted isn't an error to retry. Fall back to the invoices your app created. If it helps your users, let the practitioner know they can accept the Terms in the Onfire portal.

Webhooks: a client block, and more events

Once a practice has accepted the Terms:

Each invoice.* and checkout_session.* payload gains a client object. data.invoice.client is {name, email, phone, billing_address}, and data.checkout_session.client is {name, email, phone}. On records your app created, it's the whole guest as you supplied it. On anything else, phone and billing_address are null.

{
  "id": "evt_…",
  "type": "invoice.paid",
  "api_version": "2026-06-01",
  "created": 1782755598,
  "data": {
    "invoice": {
      "partner_public_id": "prt_fixture00000000000000000000000001",
      "invoice_public_id": "inv_fixture00000000000000000000000001",
      "status": "paid",
      "external_invoice_ref": "your-invoice-ref-0001",
      "external_client_ref": "your-client-ref-0001",
      "amount": "2050.00",
      "currency": "USD",
      "ref_id": "rc_fixture01",
      "payment_reference": "pay_fixture0000000000000001",
      "client": {
        "name": "Example Guest",
        "email": "guest@example.com",
        "phone": "+15555550100",
        "billing_address": {"line1": "1 Example Street", "line2": null, "city": "Springfield",
                            "state": "CA", "postal_code": "90000", "country": "US"}
      }
    }
  }
}

client is absent, not null, for a practice that hasn't accepted. Its presence is the signal that the practice has accepted. Inside it, a field your app may not see is null.

More events reach you. invoice.* and checkout_session.* events now go to every app the practice has connected and that has an active endpoint, not only to the app that created the record. They begin once Onfire has seen your app act for that practice: a record you created, or any API call with the practice's token. Calling GET /api/v1/meta/partners/me right after connecting is enough.

Tell your records from other apps'

This is the change most likely to need code. An event for a record your app didn't create arrives at the same endpoint as your own, and it carries the other app's references: its external_invoice_ref, external_client_ref and client_reference_id. Those can equal values your app uses. If your receiver matches events on your own reference alone, another app's invoice.paid could mark one of your invoices paid.

The sign is a null id:

EventYoursAnother app'sKey another app's record on
invoice.*invoice_public_id setinvoice_public_id: null, and invoice_id (UUID) presentinvoice_id
checkout_session.*public_id setpublic_id: null and invoice_public_id: nullid

Here is another app's invoice event, as your endpoint receives it:

"invoice": {
  "partner_public_id": "prt_fixture00000000000000000000000001",
  "invoice_public_id": null,
  "invoice_id": "00000000-0000-4000-8000-0000000000a1",
  "status": "paid",
  "external_invoice_ref": "your-invoice-ref-0001",
  "external_client_ref": "your-client-ref-0001",
  "amount": "2050.00",
  "currency": "USD",
  "ref_id": "rc_fixture01",
  "payment_reference": "pay_fixture0000000000000001",
  "client": {"name": "Example Guest", "email": "guest@example.com", "phone": null, "billing_address": null}
}

Store these events like any other (Guide §7.5): your events table is your history, even if your product doesn't use other apps' records yet.

What hasn't changed

  • No version bump. The change is additive: api_version stays 2026-06-01, and nothing existing changes shape. Make sure your parser accepts a new key (client) and new events without failing; Guide §11 already allows additive changes.
  • Subscriptions still apply. checkout_session.* events reach an endpoint only if it subscribes to them by name. Email partner@onfirehealth.com if yours doesn't.
  • subscription.* events are unchanged for now.
  • Signing and delivery (Guide §7.4, §7.5) are unchanged.

A Checkout Session change that applies to every practice

Whether or not a practice has accepted, a Checkout Session's guest phone now goes only to the app that created the session. Name and email still reach every connected app. If your app reads client.phone from Checkout Sessions it didn't create, it receives null from October 28, 2026.

Revoking

When a practice that has accepted disconnects your app, you stop receiving its data at once, including queued webhook deliveries that carry a client block (one queued before the practice accepted has none, and can still arrive). For any practice, a ?scope=all read with a token issued before the disconnect is a 403: The practitioner revoked this Connected App; they must connect it again. Reconnecting means a new consent, and a new token.

Your responsibilities

  • Guest names and emails are personal data. Protect what you receive as the Partner Terms require (sections 2.4 and 7.5). Keep it out of logs and test data, and store only what your product needs. Remember that your stored webhook bodies contain it too.
  • Don't put clinical or health information in metadata, or in any other field you send us (section 7.5(b)). Once a practice accepts, a Checkout Session's metadata reaches every app the practice has connected.

The Terms are at onfirehealth.com/partner-agreement, and that page is the authoritative version.

Checklist

  1. Accept a client key on invoice.* and checkout_session.* events, and new event types, without failing.
  2. Tell your records from other apps' by the null id, and key other apps' records on invoice_id or id, never on their external_*_ref or client_reference_id.
  3. Use ?scope=all only where you need other apps' invoices; reconcile your own with the default scope.
  4. Handle a 403 on ?scope=all as "this practice hasn't accepted yet", not as a failure.
  5. Stop reading client.phone from Checkout Sessions your app didn't create.
  6. Treat everything in client and client_* as personal data, including in stored bodies.
  7. Make sure Onfire has seen your app act for each practice, for example by calling GET /api/v1/meta/partners/me after connecting.

Building with an AI coding agent?

Version 0.3.0 of the Onfire partner integration skill covers all of this. It includes signed example deliveries for practices that have accepted, both for the app that created the record and for another app, so your receiver can be tested against them. Ask your Onfire contact for it. The Onfire Zapier app is also being updated to match.

Questions? Email partner@onfirehealth.com or your Onfire contact.