> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getruba.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sell Access by Team Seat

> Let one billing owner buy and assign access to multiple people

export const RubaSocials = () => {
  if (typeof document === "undefined") {
    return null;
  }
  const inject = () => {
    const footer = document.getElementById("footer");
    if (!footer || document.getElementById("ruba-socials")) {
      return false;
    }
    const isDark = document.querySelector("html.dark") || document.querySelector('html[class*="dark"]');
    const lineColor = isDark ? "#383838" : "#e8e8ed";
    const iconColor = isDark ? "#8e8e93" : "#86868b";
    const markColor = isDark ? "#ffffff" : "#08080c";
    const hoverColor = "#0071e3";
    const div = document.createElement("div");
    div.id = "ruba-socials";
    div.style.cssText = `display:flex;align-items:center;gap:20px;padding-top:20px;margin-top:20px;border-top:1px solid ${lineColor};width:100%`;
    const logo = `<a href="https://getruba.com" style="display:inline-flex;height:24px;align-items:center;gap:8px;text-decoration:none;color:${markColor};line-height:1;"><svg width="24" height="24" viewBox="0 0 120 120" aria-hidden="true" style="display:block;flex:none;"><g transform="translate(2.5 0)"><path d="M19 25h59L65.43 47H19a4 4 0 0 1-4-4V29a4 4 0 0 1 4-4Z" fill="${markColor}"/><path d="M19 73h31.57L38 95H19a4 4 0 0 1-4-4V77a4 4 0 0 1 4-4Z" fill="${markColor}"/><path d="M85 25h25L70 95H45Z" fill="#007AFF"/></g></svg><span style="display:inline-flex;height:24px;align-items:center;font-weight:600;font-size:15px;color:${markColor};letter-spacing:-0.01em;line-height:1;">Ruba</span></a>`;
    const spacer = `<div style="flex:1"></div>`;
    const ig = `<a href="https://instagram.com/getruba" target="_blank" rel="noopener" aria-label="Instagram" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"/></svg></a>`;
    const li = `<a href="https://linkedin.com/company/getruba" target="_blank" rel="noopener" aria-label="LinkedIn" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/></svg></a>`;
    const x = `<a href="https://x.com/getruba" target="_blank" rel="noopener" aria-label="X" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg></a>`;
    const github = `<a href="https://github.com/rubadot" target="_blank" rel="noopener" aria-label="GitHub" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .7a11.5 11.5 0 0 0-3.64 22.4c.58.1.79-.25.79-.56v-2.24c-3.22.7-3.9-1.37-3.9-1.37-.53-1.34-1.29-1.7-1.29-1.7-1.05-.72.08-.71.08-.71 1.16.08 1.78 1.2 1.78 1.2 1.04 1.77 2.72 1.26 3.38.96.1-.75.4-1.26.74-1.55-2.57-.3-5.27-1.29-5.27-5.69 0-1.26.45-2.29 1.19-3.09-.12-.29-.52-1.47.11-3.05 0 0 .97-.31 3.16 1.18A10.96 10.96 0 0 1 12 6.09c.98 0 1.94.13 2.86.39 2.2-1.49 3.16-1.18 3.16-1.18.63 1.58.23 2.76.11 3.05.74.8 1.19 1.83 1.19 3.09 0 4.42-2.7 5.39-5.28 5.68.42.36.79 1.06.79 2.14v3.28c0 .31.21.67.8.56A11.5 11.5 0 0 0 12 .7Z"/></svg></a>`;
    div.innerHTML = logo + spacer + ig + li + x + github;
    footer.appendChild(div);
    return true;
  };
  if (!document.documentElement.dataset.rubaSocialsObserver) {
    document.documentElement.dataset.rubaSocialsObserver = "true";
    const observer = new MutationObserver(() => {
      if (!document.getElementById("ruba-socials")) {
        inject();
      }
    });
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }
  setTimeout(() => {
    if (!inject()) {
      const interval = setInterval(() => {
        if (inject()) {
          clearInterval(interval);
        }
      }, 500);
      setTimeout(() => clearInterval(interval), 10000);
    }
  }, 300);
  return null;
};

<link rel="stylesheet" href="/style.css" />

<RubaSocials />

Seat-based products separate the person paying from the people using the product. A billing manager purchases capacity, assigns it to members, and each claimed seat receives its own benefit grants.

## The three records involved

| Record           | Role in the model                                                                                                   |
| ---------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Customer**     | Owns payment methods, orders, and subscriptions. Its first seat purchase permanently changes its type to `team`.    |
| **Member**       | Represents an individual user. Members have an email and one of `owner`, `billing_manager`, or `member` roles.      |
| **CustomerSeat** | Connects one member to one product and records `pending`, `claimed`, or `revoked` status plus its invitation token. |

The purchaser becomes an `owner`. Owners and billing managers can administer seats and subscriptions. Product access belongs to the member grant, so your application should identify the person by member rather than by the customer that paid.

## Subscription or perpetual access

| Decision         | Subscription seats                    | One-time seats            |
| ---------------- | ------------------------------------- | ------------------------- |
| Payment          | Monthly or yearly                     | Single charge             |
| Access lifetime  | While the subscription remains active | Does not expire           |
| More capacity    | Change subscription quantity          | Buy another order         |
| Benefit lifetime | Active subscription period            | After the seat is claimed |

## Configure the offer

<Steps>
  <Step title="Create the product">
    Start a [new product](https://getruba.com/to/dashboard/products/new), then add its name, description, and media.
  </Step>

  <Step title="Choose the commercial model">
    Select subscription or one-time, choose the billing cycle for subscriptions, and set pricing to **Seat-based**.
  </Step>

  <Step title="Define the seat curve">
    Use a fixed price for one rate per seat. Use graduated tiers when each quantity range keeps its own rate. Use volume tiers when the total quantity selects one rate for every seat.
  </Step>

  <Step title="Attach member benefits">
    Add what each seat holder receives. Benefits are issued on claim, not merely when capacity is purchased.
  </Step>
</Steps>

For tiers of 1–10 seats at $10 and 11+ at $8, fourteen **graduated** seats cost `10 × $10 + 4 × $8 = $132`. Fourteen **volume** seats cost `14 × $8 = $112`.

<Info>
  With Ruba's default confirmation page, the purchaser's seat is claimed automatically. A custom `success_url` skips that automatic claim; assign the purchaser through the portal or API if they also need product access.
</Info>

## Seat lifecycle

* **Pending** means an invitation was sent but not claimed.
* **Claimed** means the member has the benefits.
* **Revoked** removes those benefits and makes the capacity assignable again.

Managers can assign by email, external customer ID, or Ruba customer ID; resend a pending invitation; revoke an assignment; and change subscription quantity. Invitation links remain valid for 24 hours.

<Warning>
  Revoking an assignment does not lower the paid subscription quantity. To stop paying for unused capacity, reduce the seat count as a separate action.
</Warning>

## Quantity changes and proration

Adding subscription seats creates an immediate prorated charge for the remainder of the current period, then bills the full quantity on the next cycle. Reducing quantity creates a prorated credit for the unused part of the period.

## Current limits

* Dashboard assignment is one seat at a time; use the API for bulk workflows.
* A subscription supports at most 1,000 seats.
* The billing manager does not receive product benefits solely because of that role.
* Seat metadata supports 10 keys and 1 KB in total.

Continue with the API and webhook implementation in [Build Seat-Based Pricing](/guides/seat-based-pricing).
