Skip to main content
If you sell seat-based products on Ruba, your organization will move over to the member model. This guide spells out what shifts, what breaks, and exactly how to adapt your code.

What the member model brings

The member model introduces a split between who pays and who uses through three entities:
  • A Customer is the billing entity (who pays). They own subscriptions, orders, and payment methods. After migration, customers with seat-based products are upgraded to type: "team".
  • A Member is a person under a customer (who uses). Each member has their own email and role (owner, billing_manager, or member), and receives benefit grants independently.
  • A CustomerSeat is the link between a subscription and a member. It tracks assignment status and carries metadata.
Today, each seat holder is represented as their own Customer. After migration, seat holders become Member records under the purchasing customer, and CustomerSeat links the subscription to each member. For a complete overview of how these entities fit together, see the Seat-Based Pricing guide.

How the migration plays out: two phases

The migration plays out in two phases so you can adapt your integration before the breaking change lands.

Phase 1: Preparation (non-breaking)

In this phase, Ruba creates Member records for all your existing seat holders and populates member_id on seats and benefit grants. Nothing breaks because customer_id on seats still points to the seat holder, and no customers are deleted. This buys you time to start reading the new member and member_id fields and adapt your code before the breaking change.

Phase 2: Member model enabled (breaking)

In this phase, the member model is fully switched on. customer_id on seats and grants flips to point to the billing customer (the buyer), old seat-holder customer records are deleted, and all new operations lean on the member model. This is the breaking change. If your code reads customer_id to identify seat holders, it will now get the buyer instead.

Breaking changes at a glance

These shifts happen in Phase 2 only:

How to adapt your code

Adapt your code during Phase 1 so everything works when Phase 2 switches on. The member field is available in both phases.

1. Seats: identify holders by member, not customer_id

Calling GET /v1/customer-seats/{seat_id} or listing seats for a subscription returns: Phase 1 (non-breaking, customer_id still points to Alice):
Phase 2 (breaking, customer_id flipped to Jane):
  • Use seat.member or seat.email to identify who holds the seat. This works in both phases.
  • seat.customer_email still resolves to the holder’s email, so it is safe to keep using.

2. Benefit grants: use grant.member for the recipient

Calling GET /v1/benefit-grants/{grant_id} or receiving a benefit_grant.created webhook returns: Phase 1:
Phase 2:
  • Use grant.member to know who received the benefit. This is consistent across both phases.
  • License keys and downloads tied to this grant also transfer in Phase 2.

3. Replace stored seat-holder customer IDs

If you stored cust_alice or cust_bob in your database to track who has access, those IDs return 404 after Phase 2. During Phase 1, use the seat list endpoint to map old references to new member IDs:

4. Update webhook handlers

Seat webhooks:
Benefit grant webhooks:
New member lifecycle events are now available if you want to track when team members join or leave:
  • member.created is emitted when a member joins the team (via seat assignment or customer creation)
  • member.updated is emitted when a member’s details change
  • member.deleted is emitted when a member is removed

5. Seat assignment

Existing assignment calls using email, customer_id, or external_customer_id keep working. Ruba resolves them to a member internally. However, the response shifts between phases: Phase 1: seat.customer_id in the response still points to the seat holder.
Phase 2: seat.customer_id in the response points to the buyer, not the seat holder.
If your code reads customer_id from the assignment response, switch to seat.member instead. You can also assign by member ID directly:
Do not mix legacy identifiers (customer_id, external_customer_id) with member identifiers (member_id, external_member_id) in the same request.

6. Pass member_id when creating customer sessions

After migration, pass a member_id when creating customer sessions to scope the portal view to a specific member:
You can also use external_member_id as an alternative to member_id:
  • Owners and billing managers see full team management, including assigning seats, managing members, and adjusting seat count.
  • Regular members see only their own benefits and account details.
For team customers, member_id is required. For individual customers, it is optional. When omitted, the owner member is used automatically.

Migration checklist

During Phase 1 (do this now):
  • Adapt seat-handling code to use seat.member / seat.email instead of seat.customer_id
  • Adapt benefit grant code to use grant.member instead of grant.customer_id
  • Replace any stored seat-holder customer IDs with member IDs
  • Update webhook handlers to read member from seat and grant payloads
  • Pass member_id when calling customerSessions.create() for portal access
Optional enhancements:
  • Use member_id or external_member_id in seat assignment calls
  • Subscribe to member.created, member.updated, member.deleted webhooks

A real-world example

Imagine you sell a Team Pro plan. Jane (the manager) buys 3 seats and hands them out to Alice and Bob.

Before migration (current state)

Your code probably does something like this to grant access:
This works because customer_id on the grant points to Alice directly.

After Phase 1: Preparation (non-breaking)

Members are created, member_id is populated on seats and grants, but customer_id still points to the seat holder. Your existing code keeps working.
What you can do now: Start reading seat.member and grant.member in your code. Both the old customer_id and the new member fields are available, so you can adapt your integration at your own pace.

After Phase 2: Member model enabled (breaking)

customer_id flips to the billing customer. Old seat-holder customers are deleted.
Now every customer_id is cust_jane, the buyer. Your old webhook handler would grant access to Jane instead of Alice and Bob.