Management Services API (0.0.0)

Download OpenAPI specification:

Tenant admin access

Actions available to tenant admins within their own tenant. Some are also available to Qvyon Operations.

Invite a new User to a tenant

Create a new user and send an Auth0 invitation email.

The user is created in PENDING status. An async job sends the Auth0 invitation. Once the user accepts the invite and logs in, their status transitions to ACTIVE.

Authorization (requires mgmt:write:users scope):

  • Admin Users: Invites user to their tenant (from JWT org_id). X-Tenant-ID header ignored.
  • Operations Users: Invites user to specified tenant. X-Tenant-ID header required.

Returns 409 if a user with the same email already exists in this tenant.

Authorizations:
OAuth2Auth
header Parameters
X-Tenant-ID
string

Auth0 Organization ID of the target tenant (e.g., 'org_abc123'). Required for Operations Users on tenant-scoped operations.

Request Body schema: application/json
required
email
required
string

Email address of the user. Must be unique within the tenant.

name
required
string

Display name of the user.

role
required
string
Enum: "admin" "member" "operations"

Role to assign to the user.

Responses

Request samples

Content type
application/json
{
  • "email": "string",
  • "name": "string",
  • "role": "admin"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "id": "string",
  • "tenant_id": "string",
  • "auth0_id": "string",
  • "email": "string",
  • "name": "string",
  • "role": "admin",
  • "status": "active",
  • "invited_at": "2019-08-24T14:15:22Z",
  • "invite_expires_at": "2019-08-24T14:15:22Z"
}

Resend invitation to a pending User

Resend the Auth0 invitation email for a user in PENDING or INVITE_FAILED status.

Deletes any existing Auth0 invitation and creates a new one with a fresh expiration. Updates invited_at timestamp.

Authorization (requires mgmt:write:users scope):

  • Admin Users: Can resend invites for users in their tenant. X-Tenant-ID header ignored.
  • Operations Users: Can resend invites for users in any tenant. X-Tenant-ID header required.

Returns 404 if user not found. Returns 409 if user is not in PENDING or INVITE_FAILED status.

Authorizations:
OAuth2Auth
path Parameters
userId
required
string
header Parameters
X-Tenant-ID
string

Auth0 Organization ID of the target tenant (e.g., 'org_abc123'). Required for Operations Users on tenant-scoped operations.

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "id": "string",
  • "tenant_id": "string",
  • "auth0_id": "string",
  • "email": "string",
  • "name": "string",
  • "role": "admin",
  • "status": "active",
  • "invited_at": "2019-08-24T14:15:22Z",
  • "invite_expires_at": "2019-08-24T14:15:22Z"
}

Get a Tenant by slug

Retrieve a single tenant by slug.

Authorization: a tenant user reading their own tenant (mgmt:read:tenant), or an Operations User reading any tenant (mgmt:read:tenants_mgmt). Holding the first scope is not enough to read someone else's tenant: the handler compares the caller's organization to the tenant's and returns 403 otherwise.

Returns 404 if not found.

Authorizations:
OAuth2AuthOAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "public_id": "eb3ce6c7-ce7e-4638-a072-e5054706f601",
  • "auth0_org_id": "string",
  • "selected_plan_key": "string",
  • "slug": "string",
  • "name": "string",
  • "description": "string",
  • "distributorCode": "string",
  • "tenant_type": "regular",
  • "status": "active",
  • "status_updated_at": "2019-08-24T14:15:22Z"
}

Ask Operations for an enterprise contract

Records a customer's interest and tells Operations about it. Nothing is provisioned and no contract exists until Operations records one.

Accepted rather than created: the answer a customer is waiting for is a conversation with sales, not a resource. Submitting a second time is allowed - a request with more detail than the last one is worth having, and refusing it would leave the customer with no way to correct a mistake.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
organizationSize
required
string <= 200 characters

How large the organization is, as the customer described it.

sequenceVolume
required
string <= 200 characters

How much sequence data they expect to process.

seatRequirement
required
string <= 200 characters

How many seats they expect to need.

deploymentType
required
string <= 200 characters

Cloud, on-premise or hybrid, as the customer described it.

specialRequirements
required
string <= 4000 characters

Anything else they want sales to know. May be empty.

Responses

Request samples

Content type
application/json
{
  • "organizationSize": "string",
  • "sequenceVolume": "string",
  • "seatRequirement": "string",
  • "deploymentType": "string",
  • "specialRequirements": "string"
}

Response samples

Content type
application/json
{
  • "errors": [
    ],
  • "type": "string",
  • "title": "string",
  • "status": 0,
  • "detail": "string",
  • "instance": "string"
}

Get a tenant's current license

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "tenantSlug": "string",
  • "plan": {
    },
  • "status": "trialing",
  • "isTrial": true,
  • "isCustom": true,
  • "hasPaidSubscription": true,
  • "hasBillingAccount": true,
  • "currentPeriodStart": "2019-08-24T14:15:22Z",
  • "currentPeriodEnd": "2019-08-24T14:15:22Z",
  • "billingInterval": "month",
  • "cancelAtPeriodEnd": true,
  • "pendingChange": {
    }
}

Get a tenant's billing contact information

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "name": "string",
  • "email": "string",
  • "taxId": "string",
  • "address": {
    }
}

Update a tenant's billing contact information

Writes directly to the Stripe Customer backing this tenant's subscription — there is no local copy.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
name
required
string
email
required
string
taxId
required
string
required
object (ManagementResources.BillingAddress)

A tenant's billing address, stored on the Stripe Customer.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "email": "string",
  • "taxId": "string",
  • "address": {
    }
}

Response samples

Content type
application/json
{
  • "name": "string",
  • "email": "string",
  • "taxId": "string",
  • "address": {
    }
}

Cancel a subscription at the end of the current billing period

Cancels at period end rather than immediately: the customer has paid for the period already, and access should run to the end of it.

The local row is not deleted. subscriptions_tenant_unique means one subscription per tenant, so the row has to survive for the tenant to ever subscribe again; Stripe's customer.subscription.deleted webhook moves it to 'canceled' when the period actually closes. Until then the subscription stays 'active' with cancelAtPeriodEnd set, which is what the customer sees.

Idempotent: cancelling an already-cancelling subscription returns the same state rather than erroring.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "tenantSlug": "string",
  • "plan": {
    },
  • "status": "trialing",
  • "isTrial": true,
  • "isCustom": true,
  • "hasPaidSubscription": true,
  • "hasBillingAccount": true,
  • "currentPeriodStart": "2019-08-24T14:15:22Z",
  • "currentPeriodEnd": "2019-08-24T14:15:22Z",
  • "billingInterval": "month",
  • "cancelAtPeriodEnd": true,
  • "pendingChange": {
    }
}

Change an existing subscription's plan tier and/or billing interval

Changing the plan tier within the same billing interval takes effect immediately: Stripe prorates the difference and invoices it against the saved payment method. Capacity the customer asked for today should not wait a month.

Changing the billing interval - monthly to yearly or back - is scheduled for the end of the current period instead. Doing it immediately would refund part of a month already used and start the new period on an arbitrary date, and no allowance is credited on a mid-cycle change, so the customer would pay the annual price and receive nothing until the year was up. The response carries subscription.pendingChange and no client secret, because nothing is charged today.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
planKey
required
string
billingInterval
required
string (ManagementResources.BillingInterval)
Enum: "month" "year"

Which cadence a subscription is billed on.

Responses

Request samples

Content type
application/json
{
  • "planKey": "string",
  • "billingInterval": "month"
}

Response samples

Content type
application/json
{
  • "subscription": {
    },
  • "clientSecret": "string"
}

Cancel a scheduled plan change before it takes effect

Drops a pending billing-interval change, leaving the subscription on what it is already paying for. Only meaningful while pendingChange is set; once the period has turned over the change is the subscription and reversing it is an ordinary change in the other direction.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "tenantSlug": "string",
  • "plan": {
    },
  • "status": "trialing",
  • "isTrial": true,
  • "isCustom": true,
  • "hasPaidSubscription": true,
  • "hasBillingAccount": true,
  • "currentPeriodStart": "2019-08-24T14:15:22Z",
  • "currentPeriodEnd": "2019-08-24T14:15:22Z",
  • "billingInterval": "month",
  • "cancelAtPeriodEnd": true,
  • "pendingChange": {
    }
}

List a tenant's Stripe invoice history

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Start or change a paid subscription

Creates the Stripe Customer + Subscription server-side (payment_behavior: default_incomplete) and returns a client secret for the frontend to confirm the card directly with Stripe. Card data never passes through our backend.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
planKey
required
string

Responses

Request samples

Content type
application/json
{
  • "planKey": "string"
}

Response samples

Content type
application/json
{
  • "subscriptionId": "string",
  • "clientSecret": "string"
}

Get a tenant's current token balance

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "tenantSlug": "string",
  • "balance": 0,
  • "allocatedTokens": 0,
  • "consumedTokens": 0
}

List a tenant's token ledger entries

Append-only audit trail of every token movement for this tenant — grants, expiries, purchases, and job-driven debits from recorded usage events. Default sort: created_at DESC (newest first).

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
query Parameters
limit
integer <int32> [ 1 .. 200 ]
Default: 50

Maximum items to return. Server default: 50. Server maximum: 200.

cursor
string

Opaque continuation token from previous response.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": {
    }
}

Start a token top-up purchase

Creates a Stripe PaymentIntent for a one-off token purchase and returns its client secret, so the browser confirms the card directly with Stripe and no card data reaches this service.

Tokens are NOT credited here. The balance moves only once Stripe confirms the charge, when payment_intent.succeeded arrives at the webhook and writes a purchase entry to the ledger. A caller who never confirms, or whose card is declined, leaves no ledger movement at all - which is why this returns an intent rather than a balance.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
tokens
required
integer <int32> >= 1

How many tokens to buy. The charge is priced server-side from the configured top-up rate - the client never states what it should be charged, so a tampered request can buy fewer tokens but never cheaper ones.

Responses

Request samples

Content type
application/json
{
  • "tokens": 1
}

Response samples

Content type
application/json
{
  • "clientSecret": "string",
  • "tokens": 0,
  • "amountCents": 0,
  • "currency": "string"
}

Get the current token top-up price

What tokens cost to buy on top of the plan allotment, so a client can show the price while the customer chooses an amount.

For display only. A purchase is always priced again server-side from the same configuration, so a stale or tampered copy of this cannot change what a card is charged.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "microsPerToken": 0,
  • "currency": "string",
  • "minTokens": 0,
  • "maxTokens": 0
}

Summarize a tenant's token consumption by the person who ran it

Aggregates recorded usage events per user, so a tenant admin can see who spent the organisation's tokens rather than only that the organisation did.

Lifetime totals, for the same reason usage-summary is: usage_events carries no period marker.

Attribution only exists from the point mimic-api began reporting a submitter. Earlier consumption is real and is still counted, in a single entry with no user attached, so the entries continue to sum to the tenant total rather than quietly disagreeing with it.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "entries": [
    ],
  • "totalTokens": 0
}

Summarize a tenant's consumption over time

Token consumption bucketed over time, for the usage chart. Hourly buckets for the 24h range, daily for the rest.

Bucketed on when the work completed rather than when the event was recorded, so a backlog drained after an outage lands on the hours the jobs actually ran. Events that report no completion time fall back to their record time.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
query Parameters
range
string (ManagementResources.UsageHistoryRange)
Enum: "24h" "7d" "30d" "90d"

How far back a usage history chart reaches.

Responses

Response samples

Content type
application/json
{
  • "range": "24h",
  • "points": [
    ]
}

Summarize a tenant's token consumption by usage category

Aggregates recorded usage events into per-category totals, so a dashboard can show where a tenant's tokens went rather than only how many are left.

Totals are lifetime, not per billing period: usage_events carries no period marker, and deriving one from the subscription's current period would silently exclude history from before the current plan.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "entries": [
    ],
  • "totalTokens": 0
}

List Users

List users within the target tenant.

Authorization (requires mgmt:read:users scope):

  • Admin Users: Lists users in their tenant (from JWT org_id). X-Tenant-ID header ignored.
  • Operations Users: Lists users in specified tenant. X-Tenant-ID header required.

Default sort: created_at DESC, email ASC (newest first, stable tie-breaker).

Authorizations:
OAuth2Auth
query Parameters
limit
integer <int32> [ 1 .. 200 ]
Default: 50

Maximum items to return. Server default: 50. Server maximum: 200.

cursor
string

Opaque continuation token from previous response.

header Parameters
X-Tenant-ID
string

Auth0 Organization ID of the target tenant (e.g., 'org_abc123'). Required for Operations Users on tenant-scoped operations.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": {
    }
}

Get a User by ID

Retrieve a single user by ID.

Authorization (requires mgmt:read:users scope):

  • Admin Users: Can only read users in their tenant. X-Tenant-ID header ignored.
  • Operations Users: Can read users in any tenant. X-Tenant-ID header required.

Returns 404 if user not found or not in target tenant.

Authorizations:
OAuth2Auth
path Parameters
userId
required
string
header Parameters
X-Tenant-ID
string

Auth0 Organization ID of the target tenant (e.g., 'org_abc123'). Required for Operations Users on tenant-scoped operations.

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "id": "string",
  • "tenant_id": "string",
  • "auth0_id": "string",
  • "email": "string",
  • "name": "string",
  • "role": "admin",
  • "status": "active",
  • "invited_at": "2019-08-24T14:15:22Z",
  • "invite_expires_at": "2019-08-24T14:15:22Z"
}

Update a User

Update an existing user's name, role, or status.

Authorization (requires mgmt:write:users scope):

  • Admin Users: Can update users in their tenant. X-Tenant-ID header ignored.
  • Operations Users: Can update users in any tenant. X-Tenant-ID header required.

Note: Cannot transition status to PENDING or INVITE_FAILED via this endpoint.

Returns 404 if user not found or not in target tenant.

Authorizations:
OAuth2Auth
path Parameters
userId
required
string
header Parameters
X-Tenant-ID
string

Auth0 Organization ID of the target tenant (e.g., 'org_abc123'). Required for Operations Users on tenant-scoped operations.

Request Body schema: application/json
required
name
string

Display name of the user. Omit to leave unchanged.

role
string
Enum: "admin" "member" "operations"

Role of the user within the tenant. Omit to leave unchanged.

status
string
Enum: "active" "deactivated" "suspended" "pending" "invite_failed"

Current status of the user. Omit to leave unchanged. Cannot transition to PENDING or INVITE_FAILED.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "role": "admin",
  • "status": "active"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "id": "string",
  • "tenant_id": "string",
  • "auth0_id": "string",
  • "email": "string",
  • "name": "string",
  • "role": "admin",
  • "status": "active",
  • "invited_at": "2019-08-24T14:15:22Z",
  • "invite_expires_at": "2019-08-24T14:15:22Z"
}

Deactivate a User

Soft-delete a user (sets status to DEACTIVATED).

Authorization (requires mgmt:write:users scope):

  • Admin Users: Can delete users in their tenant. X-Tenant-ID header ignored.
  • Operations Users: Can delete users in any tenant. X-Tenant-ID header required.

Idempotent: returns 204 even if already deactivated or not found.

Authorizations:
OAuth2Auth
path Parameters
userId
required
string
header Parameters
X-Tenant-ID
string

Auth0 Organization ID of the target tenant (e.g., 'org_abc123'). Required for Operations Users on tenant-scoped operations.

Responses

Response samples

Content type
application/json
{
  • "type": "string",
  • "title": "string",
  • "status": 0,
  • "detail": "string",
  • "instance": "string"
}

Sign a User out of every device

Revokes the user's Auth0 sessions and refresh tokens, so any access token already issued to them stops working at its next silent refresh rather than lasting until it expires on its own.

A support action, not a state change: the user stays active and may log straight back in. To stop someone logging in at all, suspend them instead, which blocks the account and revokes access together.

Authorization (requires mgmt:write:users scope):

  • Admin Users: Can sign out users in their tenant. X-Tenant-ID header ignored.
  • Operations Users: Can sign out users in any tenant. X-Tenant-ID header required.

Returns 409 for a user who has never logged in, since a pending invitation has no session to revoke.

Authorizations:
OAuth2Auth
path Parameters
userId
required
string
header Parameters
X-Tenant-ID
string

Auth0 Organization ID of the target tenant (e.g., 'org_abc123'). Required for Operations Users on tenant-scoped operations.

Responses

Response samples

Content type
application/json
{
  • "type": "string",
  • "title": "string",
  • "status": 0,
  • "detail": "string",
  • "instance": "string"
}

Qvyon Ops only

Cross-tenant management actions reserved for Qvyon Operations.

List recorded administrative actions and system events

The Operations log. Newest first.

Cross-tenant by definition, so it carries no tenant header and no slug: the point of the log is to see across tenants, and the scope restricts it to the Operations organization. Narrow it to one tenant with tenantSlug.

Paged on (occurred_at, id) rather than by offset. The log grows continuously, so an offset page shifts under the reader as rows arrive above it.

Scope: mgmt:read:tenants_mgmt, Operations Users only.

Authorizations:
OAuth2Auth
query Parameters
limit
integer <int32> [ 1 .. 200 ]
Default: 50

Maximum items to return. Server default: 50. Server maximum: 200.

cursor
string

Opaque continuation token from previous response.

category
string (ManagementResources.AuditCategory)
Enum: "audit" "security" "communication" "events"

Show only this category. Omit for all.

activityType
string

Show only this kind of activity - Tokens, Plan, Status. Omit for all.

Independent of category, and the reason it exists: a token movement is an administrative action when an operator grants it and a system event when a payment clears, so "everything that moved this tenant's balance" cuts across both categories and cannot be expressed by category alone.

tenantSlug
string

Show only events concerning this tenant. Omit for all tenants.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": {
    }
}

List every enterprise contract (Operations only)

Soonest to run out first, because the view exists to answer which renewals are coming up.

Each row carries the organization it belongs to, joined server-side rather than fetched per row - the same N+1 the ops registry had to fix.

Authorizations:
OAuth2Auth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Get one organization's contract (Operations only)

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "tenantSlug": "string",
  • "organization": "string",
  • "valueCents": 0,
  • "currency": "string",
  • "billingFrequency": "monthly",
  • "tokenPool": 0,
  • "seats": 0,
  • "startsOn": "2019-08-24",
  • "endsOn": "2019-08-24",
  • "discountBasisPoints": 0,
  • "seatPriceCents": 0,
  • "deployment": "cloud",
  • "licencePriceCents": 0,
  • "tokenBonus": 0,
  • "unitPriceMicros": 0,
  • "tokenDiscountBasisPoints": 0,
  • "licenseDiscountBasisPoints": 0,
  • "periods": [
    ],
  • "notes": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Record or revise an organization's contract (Operations only)

One contract per organization. Sending this for an organization that already has one revises those terms; a renegotiation is the same contract on new terms, not a second one.

Recording terms does not provision anything. The tenant's plan, seats and token balance still come from its subscription, which is granted separately - so a contract can be drafted and corrected before any capacity is handed out, and a typo here cannot spend real compute.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
valueCents
required
integer <int64>
currency
required
string
billingFrequency
required
string (ManagementResources.ContractBillingFrequency)
Enum: "monthly" "quarterly" "annually"

How often an enterprise customer is invoiced for their contract.

tokenPool
required
integer <int64>
seats
required
integer <int32>
startsOn
required
string <date>
endsOn
required
string <date>
discountBasisPoints
required
integer <int32> [ 0 .. 10000 ]

Discount on the whole contract, in basis points (1250 = 12.5%), applied after the component discounts. Zero when none was agreed.

seatPriceCents
required
integer <int64> >= 0

Agreed price of one seat for the term, in whole cents. Zero when licences are not priced separately.

tokenDiscountBasisPoints
required
integer <int32> [ 0 .. 10000 ]

Discount on the token component, in basis points. Zero when none was agreed.

licenseDiscountBasisPoints
required
integer <int32> [ 0 .. 10000 ]

Discount on the Mimic on-prem licence, in basis points. Never applies to user seats.

deployment
required
string (ManagementResources.ContractDeployment)
Enum: "cloud" "on_prem"

Where a contract runs. On-prem buys the Mimic licence; cloud does not.

licencePriceCents
required
integer <int64> >= 0

Yearly price of the Mimic on-prem licence, in whole cents. Zero on a cloud contract.

tokenBonus
required
integer <int64> >= 0

One-time free tokens credited at the start of the term.

unitPriceMicros
required
integer <int64> >= 0

Rate per token for a contract with no ramp schedule, in micros.

required
Array of objects (ManagementResources.UpsertContractPeriodRequest)

The ramp schedule, replaced wholesale. Empty for a flat contract.

Sent as a set rather than diffed: steps are added, removed and reordered together, and a half-applied edit would leave a gap or an overlap in the term.

notes
required
string

Responses

Request samples

Content type
application/json
{
  • "valueCents": 0,
  • "currency": "string",
  • "billingFrequency": "monthly",
  • "tokenPool": 0,
  • "seats": 0,
  • "startsOn": "2019-08-24",
  • "endsOn": "2019-08-24",
  • "discountBasisPoints": 10000,
  • "seatPriceCents": 0,
  • "tokenDiscountBasisPoints": 10000,
  • "licenseDiscountBasisPoints": 10000,
  • "deployment": "cloud",
  • "licencePriceCents": 0,
  • "tokenBonus": 0,
  • "unitPriceMicros": 0,
  • "periods": [
    ],
  • "notes": "string"
}

Response samples

Content type
application/json
{
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "tenantSlug": "string",
  • "organization": "string",
  • "valueCents": 0,
  • "currency": "string",
  • "billingFrequency": "monthly",
  • "tokenPool": 0,
  • "seats": 0,
  • "startsOn": "2019-08-24",
  • "endsOn": "2019-08-24",
  • "discountBasisPoints": 0,
  • "seatPriceCents": 0,
  • "deployment": "cloud",
  • "licencePriceCents": 0,
  • "tokenBonus": 0,
  • "unitPriceMicros": 0,
  • "tokenDiscountBasisPoints": 0,
  • "licenseDiscountBasisPoints": 0,
  • "periods": [
    ],
  • "notes": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Remove an organization's contract (Operations only)

Removes the commercial record. The tenant, its users and its subscription are untouched.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "type": "string",
  • "title": "string",
  • "status": 0,
  • "detail": "string",
  • "instance": "string"
}

Invite a Qvyon employee into the Operations organization

Create a new Operations user and send an Auth0 invitation email into the internal Qvyon Operations organization.

Unlike UserInvitesAPI, there is no tenant to target — this always invites into the Operations organization, so no X-Tenant-ID header is accepted.

Authorization: Operations Users only (requires mgmt:write:tenants_mgmt scope). Admin Users receive 403 Forbidden.

Returns 409 if a user with the same email already exists in the Operations organization.

Authorizations:
OAuth2Auth
Request Body schema: application/json
required
email
required
string

Email address of the user. Must be unique within the Operations organization.

name
required
string

Display name of the user.

Responses

Request samples

Content type
application/json
{
  • "email": "string",
  • "name": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "id": "string",
  • "tenant_id": "string",
  • "auth0_id": "string",
  • "email": "string",
  • "name": "string",
  • "role": "admin",
  • "status": "active",
  • "invited_at": "2019-08-24T14:15:22Z",
  • "invite_expires_at": "2019-08-24T14:15:22Z"
}

Create a plan (Operations only)

Creating a plan with a key that already exists is a conflict, not an update. Changing one is update, which is the call that knows a price change has to reach Stripe.

Authorizations:
OAuth2Auth
Request Body schema: application/json
required
key
required
string
name
required
string
priceCents
required
integer <int32>
billingInterval
required
string or null
yearlyPriceCents
integer or null <int32>

Price for a full year. Null when this plan has no yearly billing option.

tokenLimit
required
integer <int64>
seatLimit
required
integer <int32>
isCustom
required
boolean
topupMicrosPerToken
integer or null <int64>

Per-token price for this tier outside its allowance, in millionths of a dollar. Null or omitted falls back to the catalogue default.

isActive
boolean

Whether to offer the plan immediately. Defaults to true when omitted.

Responses

Request samples

Content type
application/json
{
  • "key": "string",
  • "name": "string",
  • "priceCents": 0,
  • "billingInterval": "string",
  • "yearlyPriceCents": 0,
  • "tokenLimit": 0,
  • "seatLimit": 0,
  • "isCustom": true,
  • "topupMicrosPerToken": 0,
  • "isActive": true
}

Response samples

Content type
application/json
{
  • "key": "string",
  • "name": "string",
  • "priceCents": 0,
  • "billingInterval": "string",
  • "yearlyPriceCents": 0,
  • "tokenLimit": 0,
  • "seatLimit": 0,
  • "isCustom": true,
  • "topupMicrosPerToken": 0,
  • "isActive": true
}

List every plan, retired ones included (Operations only)

The catalogue as Operations has to see it to edit it, which includes the plans no longer offered. The public list hides those, and editing a catalogue through a view that omits part of it is how a retired plan gets recreated under a second key.

Authorizations:
OAuth2Auth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Change a plan (Operations only)

Changes the catalogue entry and, when an amount changed, the Stripe Price behind it.

A Stripe Price is immutable, so a new amount means a new Price, which this creates and points the plan at. Without that the pricing page would show one figure and the card would be charged another - the stored amount is display only, and checkout bills whatever Price the plan points to.

Subscribers already on the old Price keep it until their next plan change. That is Stripe's own behaviour for a subscription item and is deliberate here too: repricing an existing contract without telling the customer is a commercial decision, not a side effect of editing a catalogue.

Authorizations:
OAuth2Auth
path Parameters
key
required
string
Request Body schema: application/json
required
name
required
string
priceCents
required
integer <int32>
billingInterval
required
string or null
yearlyPriceCents
required
integer or null <int32>
tokenLimit
required
integer <int64>
seatLimit
required
integer <int32>
isCustom
required
boolean
topupMicrosPerToken
required
integer or null <int64>
isActive
required
boolean

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "priceCents": 0,
  • "billingInterval": "string",
  • "yearlyPriceCents": 0,
  • "tokenLimit": 0,
  • "seatLimit": 0,
  • "isCustom": true,
  • "topupMicrosPerToken": 0,
  • "isActive": true
}

Response samples

Content type
application/json
{
  • "key": "string",
  • "name": "string",
  • "priceCents": 0,
  • "billingInterval": "string",
  • "yearlyPriceCents": 0,
  • "tokenLimit": 0,
  • "seatLimit": 0,
  • "isCustom": true,
  • "topupMicrosPerToken": 0,
  • "isActive": true
}

Create a new Tenant

Create a new tenant and corresponding Auth0 Organization.

Authorization: Operations Users only (requires mgmt:write:tenants_mgmt scope). Admin Users receive 403 Forbidden.

Returns 409 if a tenant with the same slug already exists.

Authorizations:
OAuth2Auth
Request Body schema: application/json
required
slug
required
string

Unique slug identifier for this tenant. Must be URL-safe.

name
required
string

Human-readable name of the tenant.

description
string

Optional description of the tenant.

Responses

Request samples

Content type
application/json
{
  • "slug": "string",
  • "name": "string",
  • "description": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "public_id": "eb3ce6c7-ce7e-4638-a072-e5054706f601",
  • "auth0_org_id": "string",
  • "selected_plan_key": "string",
  • "slug": "string",
  • "name": "string",
  • "description": "string",
  • "distributorCode": "string",
  • "tenant_type": "regular",
  • "status": "active",
  • "status_updated_at": "2019-08-24T14:15:22Z"
}

List Tenants

List all tenants.

Authorization: Operations Users only (requires mgmt:read:tenants_mgmt scope). Admin Users receive 403 Forbidden.

Default sort: created_at DESC, slug ASC (newest first, stable tie-breaker).

Authorizations:
OAuth2Auth
query Parameters
limit
integer <int32> [ 1 .. 200 ]
Default: 50

Maximum items to return. Server default: 50. Server maximum: 200.

cursor
string

Opaque continuation token from previous response.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": {
    }
}

Look up a tenant by its Auth0 organization ID

Resolves the org_id carried in a user's access token to the tenant it belongs to.

Exists for services that authorise by tenant and keep a local cache of them: on a cache miss they fetch the tenant here rather than depending on having received an event for it. A cache miss is self-correcting, whereas a missed event leaves a tenant permanently invisible with nothing to notice or repair it.

Returns 404 when no tenant carries that organization ID, which is a meaningful answer - the caller holds a token for an organization this platform does not know.

The organization id is a query parameter rather than a path segment: a literal segment under /v1/tenants is ambiguous against /v1/tenants/{slug}/... in Go's router, which refuses to register both.

Authorizations:
OAuth2Auth
query Parameters
auth0OrgId
required
string

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "public_id": "eb3ce6c7-ce7e-4638-a072-e5054706f601",
  • "auth0_org_id": "string",
  • "selected_plan_key": "string",
  • "slug": "string",
  • "name": "string",
  • "description": "string",
  • "distributorCode": "string",
  • "tenant_type": "regular",
  • "status": "active",
  • "status_updated_at": "2019-08-24T14:15:22Z"
}

List tenants enriched with plan, subscription status, and seat usage

Same tenants as list, joined server-side with each tenant's current plan, subscription status, and occupied seat count — for the ops organization registry view. Avoids per-tenant follow-up calls.

Authorization: Operations Users only (requires mgmt:read:tenants_mgmt scope).

Authorizations:
OAuth2Auth
query Parameters
limit
integer <int32> [ 1 .. 200 ]
Default: 50

Maximum items to return. Server default: 50. Server maximum: 200.

cursor
string

Opaque continuation token from previous response.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": {
    }
}

Update a Tenant

Update an existing tenant.

Authorization: Operations Users only (requires mgmt:write:tenants_mgmt scope). Admin Users receive 403 Forbidden.

Returns 404 if tenant not found.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
name
string

Human-readable name of the tenant.

description
string

Optional description of the tenant.

status
string
Enum: "active" "deactivated"

Current status of the tenant.

distributorCode
string

Distributor this customer came through. Send an empty string to clear it.

Absent and empty mean different things: a request that omits this leaves the code as it was, which is what lets a rename not wipe it.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "status": "active",
  • "distributorCode": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "public_id": "eb3ce6c7-ce7e-4638-a072-e5054706f601",
  • "auth0_org_id": "string",
  • "selected_plan_key": "string",
  • "slug": "string",
  • "name": "string",
  • "description": "string",
  • "distributorCode": "string",
  • "tenant_type": "regular",
  • "status": "active",
  • "status_updated_at": "2019-08-24T14:15:22Z"
}

Delete a Tenant

Soft-delete a tenant (sets status to deactivated).

Authorization: Operations Users only (requires mgmt:write:tenants_mgmt scope). Admin Users receive 403 Forbidden.

Idempotent: returns 204 even if already deleted or not found.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "type": "string",
  • "title": "string",
  • "status": 0,
  • "detail": "string",
  • "instance": "string"
}

Grant or override a tenant's license (Operations only, no Stripe)

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
planKey
required
string
currentPeriodEnd
required
string or null <date-time>
reason
required
string
trialDays
integer <int32> [ 1 .. 365 ]

Start the subscription as a trial running this many days from now, rather than as an active license. Mutually exclusive with currentPeriodEnd, which sets the end date directly.

Responses

Request samples

Content type
application/json
{
  • "planKey": "string",
  • "currentPeriodEnd": "2019-08-24T14:15:22Z",
  • "reason": "string",
  • "trialDays": 1
}

Response samples

Content type
application/json
{
  • "tenantSlug": "string",
  • "plan": {
    },
  • "status": "trialing",
  • "isTrial": true,
  • "isCustom": true,
  • "hasPaidSubscription": true,
  • "hasBillingAccount": true,
  • "currentPeriodStart": "2019-08-24T14:15:22Z",
  • "currentPeriodEnd": "2019-08-24T14:15:22Z",
  • "billingInterval": "month",
  • "cancelAtPeriodEnd": true,
  • "pendingChange": {
    }
}

End a trial immediately (Operations only)

Ends a trial now by setting its period end to the current time.

The subscription stays in the trialing status rather than moving to canceled: the tenant was on a trial and it finished, which is what the registry already renders as "Expired". Marking it canceled would claim a paid subscription was cancelled, which never happened.

Idempotent - expiring an already-expired trial returns the same result rather than moving the date backwards again.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string

Responses

Response samples

Content type
application/json
{
  • "tenantSlug": "string",
  • "plan": {
    },
  • "status": "trialing",
  • "isTrial": true,
  • "isCustom": true,
  • "hasPaidSubscription": true,
  • "hasBillingAccount": true,
  • "currentPeriodStart": "2019-08-24T14:15:22Z",
  • "currentPeriodEnd": "2019-08-24T14:15:22Z",
  • "billingInterval": "month",
  • "cancelAtPeriodEnd": true,
  • "pendingChange": {
    }
}

Extend a trial (Operations only)

Pushes a trial's end date out by the requested number of days.

Counted from the existing end date rather than from today, so extending a trial that still has time left adds to it instead of shortening it, and two extensions add up.

Only a trialing subscription can be extended - moving the period end of a paid subscription would silently disagree with Stripe, which owns that date.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
days
required
integer <int32> [ 1 .. 365 ]

Days to add to the current trial end. Counted from the existing end date, so extending twice adds both.

reason
required
string

Why the trial is being extended. Recorded for traceability.

Responses

Request samples

Content type
application/json
{
  • "days": 1,
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "tenantSlug": "string",
  • "plan": {
    },
  • "status": "trialing",
  • "isTrial": true,
  • "isCustom": true,
  • "hasPaidSubscription": true,
  • "hasBillingAccount": true,
  • "currentPeriodStart": "2019-08-24T14:15:22Z",
  • "currentPeriodEnd": "2019-08-24T14:15:22Z",
  • "billingInterval": "month",
  • "cancelAtPeriodEnd": true,
  • "pendingChange": {
    }
}

Manually credit or debit a tenant's token balance (Operations only)

Records an admin_action entry against the tenant's token ledger, for credits and manual corrections. Distinct from job-driven debits, which arrive through POST /v1/usage/events and are priced from usage_rates.

Authorizations:
OAuth2Auth
path Parameters
slug
required
string
Request Body schema: application/json
required
amount
required
integer <int64>

Signed amount to apply. Positive credits tokens, negative debits them.

Refused if it would take the balance outside what a 64-bit count can hold. A grant that could not be represented used to wrap to a negative balance and still report success.

reason
required
string

Responses

Request samples

Content type
application/json
{
  • "amount": 0,
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "id": "string",
  • "transactionType": "reservation",
  • "amount": 0,
  • "balanceAfter": 0,
  • "referenceType": "job",
  • "referenceId": "string",
  • "reason": "string",
  • "createdAt": "2019-08-24T14:15:22Z"
}

Get the catalogue-wide top-up price (Operations only)

The default a tenant falls back to when its plan sets no rate of its own, which is every tenant with no plan at all.

Distinct from the per-tenant topup-pricing call, which answers "what does this tenant pay" and resolves the plan rate first. This one answers "what is the default configured as", which is the question the editor asks.

Authorizations:
OAuth2Auth

Responses

Response samples

Content type
application/json
{
  • "microsPerToken": 0,
  • "currency": "string",
  • "minTokens": 0,
  • "maxTokens": 0
}

Change the catalogue-wide top-up price (Operations only)

Authorizations:
OAuth2Auth
Request Body schema: application/json
required
microsPerToken
required
integer <int64>
minTokens
required
integer <int32>
maxTokens
required
integer <int32>

Responses

Request samples

Content type
application/json
{
  • "microsPerToken": 0,
  • "minTokens": 0,
  • "maxTokens": 0
}

Response samples

Content type
application/json
{
  • "microsPerToken": 0,
  • "currency": "string",
  • "minTokens": 0,
  • "maxTokens": 0
}

List what each kind of work costs (Operations only)

Every configured rate, in tokens per unit: compute per hour, storage per terabyte-month, reconstruction and exportation per frame, actor import per actor.

These are what the platform charges. A usage event is priced at ingestion from the rate in force at that moment, so changing one here decides what the next job costs and never restates what a tenant has already been charged.

Authorizations:
OAuth2Auth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Change what one kind of work costs (Operations only)

Addressed by category, rate key and instance type, which is how the table is keyed. A rate has no id a person would ever hold.

Authorizations:
OAuth2Auth
Request Body schema: application/json
required
category
required
string
rateKey
required
string
instanceType
required
string
tokenAmount
required
number <double> >= 0

Responses

Request samples

Content type
application/json
{
  • "category": "string",
  • "rateKey": "string",
  • "instanceType": "string",
  • "tokenAmount": 0.1
}

Response samples

Content type
application/json
{
  • "category": "string",
  • "rateKey": "string",
  • "instanceType": "string",
  • "tokenAmount": 0.1,
  • "unit": "string"
}

Get current user information

Retrieve information about the currently authenticated user.

Returns the user's profile, their tenant, and OAuth2 scopes from the JWT.

Authorization: Any authenticated user (valid JWT required, no additional scope needed).

Returns 404 if the authenticated user does not exist in the database. This can occur if:

  • The user was deleted from the database but their Auth0 session is still valid
  • The user has not been synced from Auth0 to the database yet
Authorizations:
OAuth2Auth

Responses

Response samples

Content type
application/json
{
  • "user": {
    },
  • "tenant": {
    }
}

List the plan catalog

The plan catalogue is public. It backs the plan picker on the public signup page, where the visitor has no account yet and so no token to authenticate with — requiring one made the picker render empty and left Register permanently disabled.

Nothing here is sensitive: these are the same prices, token and seat limits the marketing site publishes. Creating or changing a plan still requires mgmt:write:billing_mgmt.

Authorizations:
None

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

List the mimic-ui installers currently published

Any authenticated user can see what is available. Platforms are whatever the publisher has declared, so a new packaging format appears here without a deploy.

Authorizations:
OAuth2Auth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Get a short-lived download link for one installer

Returns a presigned URL the browser fetches directly from storage. The bucket is private and no credentials reach the client.

Minted per request rather than returned with the list: the link expires in minutes, so signing every platform on page load would hand out links that are dead before anyone clicks one.

Authorizations:
OAuth2Auth
path Parameters
platform
required
string

Responses

Response samples

Content type
application/json
{
  • "platform": "string",
  • "version": "string",
  • "sha256": "string",
  • "url": "string",
  • "expiresAt": "2019-08-24T14:15:22Z"
}

Report a batch of usage-consumption events

Records consumption facts (job duration, frame/actor counts) and turns each into a token debit against the reporting tenant, priced from usage_rates at ingestion time.

Not tenant-scoped: the caller is a service (the executor, or a script acting on its behalf) reporting on behalf of whichever tenant each event names, so tenant_slug lives in the request body rather than X-Tenant-ID.

Idempotent per event_id - a retried delivery reports 'duplicate' rather than debiting twice. Always returns 202 with a per-event result, so one bad event in a batch doesn't force retrying the whole batch.

Authorizations:
OAuth2Auth
Request Body schema: application/json
required
required
Array of objects (ManagementResources.UsageEventInput)

Responses

Request samples

Content type
application/json
{
  • "events": [
    ]
}

Response samples

Content type
application/json
{
  • "results": [
    ]
}

Hold tokens for a job that is about to start

Decides whether a job may start, and holds its estimated cost so a concurrent job cannot spend the same tokens.

This exists because reconstruction of N scenes launches N jobs at once. A caller that merely reads the balance before starting each one gets the same answer N times and starts all of them, so a tenant with room for one job can overdraw by N. Reserving writes a hold under a per-tenant lock, so requests are decided one at a time and the second sees what the first took.

Always 200. granted says whether the job may run; a refusal is a normal answer to a fair question, not an error, and it reports what was available so the caller can tell the user how short they were.

The hold is released when the job's usage event arrives carrying the same reservationId, at which point the real cost is debited instead. Billing is never the estimate.

Authorizations:
OAuth2Auth
Request Body schema: application/json
required
reservationId
required
string

Caller-generated id. The usage event for this job carries it back, which is what pairs the real cost with the hold.

tenantSlug
required
string
usageType
required
string
instanceType
string

Compute instance the job will run on, if it bills compute time.

frameCount
integer <int32>
actorCount
integer <int32>
estimatedDurationSeconds
integer <int32>

How long the caller expects the job to run. Omit it and a conservative configured default for this usage type is used instead.

Only ever used to decide whether the job may start. Billing uses the real duration reported when the job finishes, so an inaccurate estimate costs the tenant nothing.

Responses

Request samples

Content type
application/json
{
  • "reservationId": "string",
  • "tenantSlug": "string",
  • "usageType": "string",
  • "instanceType": "string",
  • "frameCount": 0,
  • "actorCount": 0,
  • "estimatedDurationSeconds": 0
}

Response samples

Content type
application/json
{
  • "reservationId": "string",
  • "granted": true,
  • "reservedTokens": 0,
  • "availableTokens": 0,
  • "refusalReason": "insufficient_tokens"
}