aria-partner-api

Purchase seats and credits

Seat purchases, credit purchases, and subscription upgrades use the partner payment method (has_payment_method). Assigning credits to a member draws from the org pool and does not charge.

idempotency_key is optional, up to 64 characters. Repeating a request with the same key returns the original response and does not charge again. Reusing the key with a different body returns 400 (error: idempotency_conflict).

Where Path What it does
Partner POST /api/v1/seats/ Buys subscription seats (charge_type "subscription")
Partner POST /api/v1/credits/ Buys unassigned on-demand credits into the org pool
Member POST /api/v1/members/{member_id}/credits/{product_id}/add/ Assigns credits from the org pool. This call does not charge
Member POST /api/v1/members/{member_id}/product/ Assigns an on-demand product from the org pool. This call does not charge
Member POST /api/v1/members/{member_id}/change_subscription/{new_product_id}/ Upgrade from Core to Advanced

Purchase seats

POST /api/v1/seats/

Buys subscription seats and adds them to available right away. An on-demand product_id returns 400 (error: not_a_subscription).

Product Billing What you get
Subscription Added to the recurring subscription, prorated to the current period (monthly or annually). Seats in use and unused seats renew automatically quantity seats. An unused seat still bills

Price comes from price_per_seat on the inventory row at the time of purchase. Purchases are final: seats cannot be refunded or released once bought, so size quantity to what you will assign. Inventory is GET /api/v1/seats/.

Request body

Field Required Type Description
product_id Yes integer Subscription product_id from seat inventory
quantity Yes integer Seats to buy. 1–1000 per request
idempotency_key No string See above

Examples

Core subscription seats:

POST /api/v1/seats/
Content-Type: application/json
Authorization: Bearer <api_key>

{
  "product_id": 101,
  "quantity": 25,
  "idempotency_key": "order-2026-09-30-core-a1"
}

Advanced subscription seats:

{
  "product_id": 103,
  "quantity": 5
}

Success (201 Created)

{
  "product_id": 101,
  "product_name": "Core",
  "description": "Scan and removal subscription on the Core catalog.",
  "charge_type": "subscription",
  "billing_frequency": "monthly",
  "quantity": 25,
  "price_per_seat": 999,
  "amount_charged": 12450,
  "currency": "usd",
  "invoice_url": "https://invoice.stripe.com/i/acct_123/inv_456",
  "purchased_at": "2026-09-30T20:15:00.000000+00:00",
  "seat_summary": {
    "product_id": 101,
    "total": 75,
    "occupied": 12,
    "available": 63
  }
}
Field Type Description
product_id, product_name, description, charge_type, billing_frequency, price_per_seat — Same values as GET /api/v1/products/
quantity integer Seats added
price_per_seat integer Unit price in cents at purchase time
amount_charged integer Prorated charge for the current period, in cents
currency string ISO 4217 code
invoice_url string Stripe-hosted receipt for this charge
purchased_at string When the purchase completed (UTC, ISO 8601)
seat_summary object Updated total, occupied, and available for the product

A repeated request with the same idempotency_key returns 200 OK with the original body.

Errors

Status Condition
400 Unknown product_id (error: product_not_configured); on-demand product_id (error: not_a_subscription); quantity missing, not an integer, or out of range; idempotency_key reused with a different body (error: idempotency_conflict)
401 Missing or invalid bearer token
402 No payment method on file (error: payment_method_required), or the charge was declined (error: payment_failed; includes Stripe’s decline_code when available)
403 Partner inactive, or the partner’s contract does not allow API purchases for this product (error: purchase_not_allowed)
409 Purchase would exceed the contractual cap for the product (error: seat_cap_exceeded; includes total, max_total)

No seats are added when the request fails.

Purchase history, invoices, and receipts are tracked in Stripe. Review them in the admin dashboard or follow invoice_url from the purchase response; the API does not list past purchases.

Purchase credits

POST /api/v1/credits/

Buys on-demand credits and adds them to the org pool, unassigned. A subscription product_id returns 400 (error: not_on_demand). The pool is GET /api/v1/credits/. Assigning credits to a member draws from available and does not charge again.

Product Billing What you get
On-demand product Charged once, at price_per_seat × quantity quantity unassigned credits in the org pool

Request body

Field Required Type Description
product_id Yes integer On-demand product_id from GET /api/v1/products/
quantity Yes integer Credits to buy. 1–1000 per request
idempotency_key No string See above

Example

POST /api/v1/credits/
Content-Type: application/json
Authorization: Bearer <api_key>

{
  "product_id": 201,
  "quantity": 25,
  "idempotency_key": "credits-2026-10-12-a1"
}

Success (201 Created)

{
  "product_id": 201,
  "product_name": "Core Scan",
  "description": "On-demand scan of the Core catalog.",
  "charge_type": "on_demand",
  "billing_frequency": "one_time",
  "quantity": 25,
  "price_per_seat": 299,
  "amount_charged": 7475,
  "currency": "usd",
  "invoice_url": "https://invoice.stripe.com/i/acct_123/inv_789",
  "purchased_at": "2026-10-12T20:15:00.000000+00:00",
  "credit_summary": {
    "product_id": 201,
    "total": 25,
    "occupied": 0,
    "available": 25
  }
}
Field Type Description
product_id, product_name, description, charge_type, billing_frequency, price_per_seat — Same values as GET /api/v1/products/
quantity integer Credits added to the pool
price_per_seat integer Unit price in cents at purchase time
amount_charged integer Charge in cents
currency string ISO 4217 code
invoice_url string Stripe-hosted receipt for this charge
purchased_at string When the purchase completed (UTC, ISO 8601)
credit_summary object Updated total, occupied, and available for the product

A repeated request with the same idempotency_key returns 200 OK with the original body.

Errors

Status Condition
400 Unknown product_id (error: product_not_configured); subscription product_id (error: not_on_demand); quantity missing, not an integer, or out of range; idempotency_key reused with a different body (error: idempotency_conflict)
401 Missing or invalid bearer token
402 No payment method on file (error: payment_method_required), or the charge was declined (error: payment_failed; includes Stripe’s decline_code when available)
403 Partner inactive, or the partner’s contract does not allow API purchases for this product (error: purchase_not_allowed)

No credits are added when the request fails.

Add credits

POST /api/v1/members/{member_id}/credits/{product_id}/add/

Assigns credits to an on-demand product already on the member. {product_id} is the on-demand catalog id. The credits are drawn from the org pool (GET /api/v1/credits/). This call does not charge. Balances on the member are in Scan credits.

POST /api/v1/members/70001/credits/201/add/
{
  "quantity": 1,
  "idempotency_key": "credits-70001-201-a1"
}
Field Required Description
quantity No Credits to assign from the org pool. Default 1
idempotency_key No See above. Repeating a request with the same key returns the original assignment and does not draw from the pool again

Success (200 OK)

{
  "member_id": 70001,
  "product_id": 201,
  "credits_remaining": 2
}

Errors

Status Condition
400 product_id is not an on-demand product on the member; quantity is not a positive integer; idempotency_key reused with a different body (error: idempotency_conflict)
401 Missing or invalid API key
402 Org pool available for that product is less than quantity, including when it is 0 (error: insufficient_credits; includes product_id and available). Buy with POST /api/v1/credits/ and retry
403 Partner inactive
404 Member or product not found
{
  "error": "insufficient_credits",
  "product_id": 201,
  "available": 0
}

No credits are assigned when the request fails.

Add a product

POST /api/v1/members/{member_id}/product/

A subscription product_id on this call returns 400 (error: use_subscription_update). Change an existing subscription with Change subscription.

quantity is credits drawn from the org pool for that on-demand product. This call does not charge.

POST /api/v1/members/70001/product/
{
  "product_id": 201,
  "quantity": 1,
  "idempotency_key": "product-70001-201-a1"
}
Field Required Description
product_id Yes An on-demand product from GET /api/v1/products/
quantity No Credits to assign from the org pool. Default 1
idempotency_key No See above. Repeating a request with the same key returns the original assignment and does not draw from the pool again

Success (200 OK)

Returns the account object plus member_id and exposures_activated. The product is assigned on that member. When is_primary is true, the account stores it in products, and in primary_subscription_product_id when the product is a subscription. exposures_activated is how many exposures moved from BLOCKED to ACTIVE when the member only had on-demand products and a subscription is added. The count is 0 when the added product is on-demand.

{
  "account_id": 12000,
  "member_id": 70001,
  "status": "ACTIVE",
  "primary_subscription_product_id": null,
  "products": [
    {
      "product_id": 201,
      "product_name": "Core Scan",
      "description": "On-demand scan of the Core catalog.",
      "charge_type": "on_demand",
      "billing_frequency": "one_time",
      "price_per_seat": 299,
      "credits_remaining": 1
    }
  ],
  "exposures_activated": 0,
  "members": [
    {
      "member_id": 70001,
      "primary_name": "Scan User",
      "primary_email": "scan@company.com",
      "is_primary": true,
      "profile_threat_score": null
    }
  ]
}

Errors

Status Condition
400 product_id missing or unknown; product_id is a subscription (error: use_subscription_update); quantity not a positive integer; idempotency_key reused with a different body (error: idempotency_conflict); account is TERMINATED
401 Missing or invalid API key
402 Org pool available for that product is less than quantity, including when it is 0 (error: insufficient_credits; includes product_id and available). Buy with POST /api/v1/credits/ and retry
403 Partner inactive
404 Member not found

Upgrade Core to Advanced

POST /api/v1/members/{member_id}/change_subscription/{new_product_id}/

The same seat moves to {new_product_id}. No seat is added and none is returned. An upgrade from Core to Advanced is charged on the partner payment method at the time of the change. The subscription resource is in Change subscription.

POST /api/v1/members/67890/change_subscription/103/
{
  "idempotency_key": "upgrade-67890-103-a1"
}
Field Required Description
idempotency_key No See above

Errors

Status Condition
400 {new_product_id} is not a subscription, is the product already on the member, or the member has no subscription (error: invalid_subscription_change); idempotency_key reused with a different body (error: idempotency_conflict); account is TERMINATED
401 Missing or invalid API key
402 No partner payment method on file (error: payment_method_required), or the charge was declined (error: payment_failed)
403 Partner inactive
404 Member or product not found