An organization under contract can have several license pools: each pool has one plan and a number of licenses. Rules place each member in a pool based on their roles, groups or labels; a fallback pool takes the members whose pool is full. An organization without pools has a single implicit pool, workspace, that covers everyone.
All routes live under https://. Scopes: licenses:read to read, licenses:write to write. What each plan includes is on the Pricing page.
Read licensesLink to the Read licenses section
GET / · licenses:read
{
"licensed": true,
"pools": [
{ "id": "8f2c1a3e-5b7d-4c9f-a1e3-5b7d9f1a3c5e", "plan_id": "<plan id>", "plan_name": "Lune", "plan_slug": "lune", "seats": 3600, "used": 3412, "source": "contract", "contract_id": "<contract id>", "contract_line_id": "<line id>", "name": null, "period_start": 1756684800, "period_end": 1788220800, "is_fallback": true, "virtual": false },
{ "id": "41bd6e8f-0a2c-4e4a-8c6e-0a2c4e6a8c0e", "plan_id": "<plan id>", "plan_name": "Galaxie", "plan_slug": "galaxie", "seats": 400, "used": 398, "source": "contract", "contract_id": "<contract id>", "contract_line_id": "<line id>", "name": null, "period_start": 1756684800, "period_end": 1788220800, "is_fallback": false, "virtual": false }
],
"rules": [
{ "id": "<rule id>", "pool_id": "41bd6e8f-0a2c-4e4a-8c6e-0a2c4e6a8c0e", "subject_type": "group", "subject_id": "<group id>", "subject_name": "Professeurs", "priority": 0 },
{ "id": "<rule id>", "pool_id": "8f2c1a3e-5b7d-4c9f-a1e3-5b7d9f1a3c5e", "subject_type": "default", "subject_id": null, "subject_name": null, "priority": 1000 }
],
"fallback_pool_id": "8f2c1a3e-5b7d-4c9f-a1e3-5b7d9f1a3c5e",
"members": 3810,
"unlicensed": 0,
"pending": 0,
"reconciling": false,
"license_version": 57,
"can_manage": true,
"manage_permission": "licenses.manage"
}| Field | Description |
|---|---|
licensed | false: no pools; pools contains only the implicit workspace pool (virtual: ). |
seats, used | The pool’s licenses (null: no limit) and the active members who hold one. |
rules | Assignment rules, in the order they apply. |
unlicensed | Active members left without a license. |
pending | Active members not placed yet. |
reconciling | The rules have changed and members are being placed. |
GET / (licenses:read) returns the license of the person who created the key: licensed, managed, org_name, pool_id, source, has_license, plan_id, plan_name, plan_slug. source says where the license comes from: manual, import, scim, invite, rule, default, fallback or unplaced. Another member’s license is shown in the member list.
Assignment rulesLink to the Assignment rules section
PUT / · licenses:write
Replaces the whole list of rules, then recalculates the pool of every member whose license comes from the rules.
| Field | Type | Required | Description |
|---|---|---|---|
rules | list | yes | Up to 200 rules; an empty list removes every rule. |
rules[].pool_id | string | yes | The target pool. |
rules[].subject_type | string | yes | role, group, label or default. |
rules[].subject_id | string | except for default | owner, admin, member, manager, billing or custom:<id> for a role; the ID of a group or a label. |
The first rule that matches a member decides; the default rule (only one) always applies last. A group rule only covers the members of the group, not its managers. Licenses chosen by hand, by import, by the directory or by an invitation do not move.
curl -X PUT https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/licenses/rules \
-H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"rules": [
{"pool_id": "<Galaxie pool>", "subject_type": "group", "subject_id": "<Professeurs group id>"},
{"pool_id": "<Lune pool>", "subject_type": "default"}
]}'import os
import requests
r = requests.put(
f"https://chat.realmoon.ai/api/v1/orgs/{os.environ['ORG_ID']}/licenses/rules",
headers={"Authorization": f"Bearer {os.environ['MOONAI_ADMIN_KEY']}"},
json={"rules": [
{"pool_id": os.environ["POOL_TEACHERS"], "subject_type": "group", "subject_id": os.environ["GROUP_TEACHERS"]},
{"pool_id": os.environ["POOL_DEFAULT"], "subject_type": "default"},
]},
timeout=60,
)
r.raise_for_status()
print(r.json()["changed"], "members changed license")const r = await fetch(`https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}/licenses/rules`, {
method: 'PUT',
headers: { Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
rules: [
{ pool_id: process.env.POOL_TEACHERS, subject_type: 'group', subject_id: process.env.GROUP_TEACHERS },
{ pool_id: process.env.POOL_DEFAULT, subject_type: 'default' },
],
}),
});
if (!r.ok) throw new Error(await r.text());
console.log((await r.json()).changed);The response has the same shape as the read, with changed: the number of members whose license changed.
| Status | code | Cause |
|---|---|---|
| 400 | invalid_request | Rule without subject_id, two default rules, two rules on the same subject, unknown pool, group, label or role. field names the field at fault. |
| 409 | no_license_pools | The organization has only one pool: every member has the same license. |
Fallback poolLink to the Fallback pool section
PUT / · licenses:write
| Field | Type | Required | Description |
|---|---|---|---|
pool_id | string | no | The pool that takes members whose pool is full; null: they stay without a license. |
Send at least {}. The response has the same shape as the read, with changed.
Assign by handLink to the Assign by hand section
POST / · licenses:write
| Field | Type | Required | Description |
|---|---|---|---|
user_ids | list | user_ids or filter | 1 to 5,000 members. |
filter | object | user_ids or filter | Active members who are in group_id, carry label_id, hold the role role and have the license license (a pool ID or none), all at once. {}: every active member. |
pool_id | string | no | A pool (kept through every recalculation), none (no license), or null (back to the rules). |
{ "filter": { "group_id": "<group id>", "license": "none" }, "pool_id": "41bd6e8f-0a2c-4e4a-8c6e-0a2c4e6a8c0e" }Response: {"changed": . skipped lists the IDs that are not active members; fallback and unplaced count the members sent to the fallback pool or left without a license when they go back to the rules.
| Status | code | Cause |
|---|---|---|
| 400 | invalid_request | user_ids and filter together, neither of them, more than 5,000 members found, unknown pool. |
| 409 | no_license_pools | The organization has only one pool. |
| 409 | pool_full | The pool does not have enough free licenses; free and requested give the numbers. Nothing is assigned. |
A member’s plan (features, limits, models) follows their license within seconds. When a pool becomes full, the license.pool_full webhook event is sent, at most once per day and per pool.
Contract and invoicesLink to the Contract and invoices section
GET / · licenses:read
Returns the organization’s contract and its invoices. The key must also have the right to read billing (or its creator must be the organization’s billing contact); otherwise the route returns 404.
contract is null when there is no active or ended contract. Its fields:
| Field | Description |
|---|---|
status | active or ended. |
channel | invoice, external or offered. |
plan, seats, lines | The main plan, the number of licenses, and one line per plan (plan, seats, price_per_seat_cents, monthly_amount_ht). |
period_start, period_end, days_left | Contract period and days left. |
billing_interval | year, semester, quarter, month or once. |
price_per_seat_cents, monthly_amount_ht, annual_amount_ht, currency | Amounts in cents, excluding VAT: price of one license per month, monthly total, total over twelve months, and the currency. |
buyer | Buyer: name, siret, vat_number, address, po_number, engagement_number, service_code, public_buyer. |
billing_emails | Email addresses that receive the invoices. |
renewal | The signed renewal, or null. |
invoices lists the last 100 invoices, most recent first:
| Field | Description |
|---|---|
number, kind | Number and type: period (billing period) or amendment. |
status, overdue | open, uncollectible, paid or void; overdue: open and past due. |
amount_ht, tax_amount, amount_ttc, currency | Amounts in cents. |
issued_at, due_at, paid_at, period_start, period_end | Dates in Unix seconds. |
hosted_url, pdf_url | Links to the invoice, only when it is open or paid. |
chorus_pro | Submission to Chorus Pro (deposited_at, reference, status), or null. |
See alsoLink to the See also section
- Licenses, in the admin console
- Members
- Organization and settings