MSPP Provisioning API — /api/v1
Status: Beta — endpoints and payloads may change. Contact us for early access.
Programmatic API for partners (distributors, resellers/MSPs) to provision org trees, mint keys, launch pentests, and pull results.
Conventions
- Base URL:
https://app.msppentesting.com/api/v1 - Auth:
Authorization: Bearer mspp_live_<key>(ormspp_test_<key>for the sandbox — no real scans, no billing). Dashboard sessions use Firebase ID tokens against the same handlers. - Format: JSON in/out. UTC ISO-8601 timestamps.
- Idempotency: mutating calls accept
Idempotency-Key: <uuid>; a repeated key returns the original result instead of acting twice (safe for automated retries). - Scoping: a key is bound to an org node and may act on that node and its subtree only.
- Pagination:
?limit=(default 50, max 200) +?cursor=; responses includenext_cursor. - Errors: standard HTTP +
{ "error": { "code", "message", "details"? } }.401bad/missing key ·403out of scope ·402quota exhausted (hard) ·409idempotency/state conflict ·422validation ·429rate/concurrency limit.
Org tree
A general tree of typed nodes: platform → distributor → reseller → tenant. Every
node carries a materialized path[] (root→self).
POST /orgs
Create a node anywhere in the tree.
// request
{
"type": "reseller", // distributor | reseller | tenant
"parentOrgId": "org_distributor",
"name": "Acme MSP",
"slug": "acme", // optional; used for portal subdomain
"tierId": "tier_pro", // optional; inherited from ancestor if omitted
"branding": { "logoUrl": "...", "primaryColor": "#0b5" }, // optional; inherited if omitted
"billing": { "mode": "inherited" } // consolidated | direct | inherited
}
// 201
{ "id": "org_acme", "type": "reseller", "parentOrgId": "org_distributor",
"path": ["org_platform","org_distributor","org_acme"], "status": "active" }
GET /orgs/{id} · GET /orgs/{id}/children · GET /orgs?subtreeOf={id}
Fetch a node, its direct children, or the whole subtree.
PATCH /orgs/{id}
Update name, status (active|suspended), tierId, branding, billing.
Quota & allocations
A pool lives on the purchasing node (usually the distributor), denominated in
per-SKU buckets (ai_pentest, external, internal, manual, …). Draw-down
is shared across the subtree; child caps are optional ceilings.
PUT /orgs/{id}/quota (pool-holding node)
{ "purchased": { "ai_pentest": 200, "external": 50 },
"replenish": "monthly", // monthly | one_time
"policy": { "ai_pentest": "soft", "external": "hard" } } // soft = allow overage & bill it
PUT /orgs/{id}/caps (child node)
{ "caps": { "ai_pentest": 40 }, "policy": { "ai_pentest": "hard" } }
GET /orgs/{id}/quota
Current pool state for the subtree.
{ "purchased": { "ai_pentest": 200 }, "reserved": { "ai_pentest": 5 },
"consumed": { "ai_pentest": 30 }, "available": { "ai_pentest": 165 } }
API keys
POST /orgs/{id}/api-keys
Mint a scoped key. The full secret is returned once; only a SHA-256 hash + prefix are stored.
// request
{ "name": "Partner provisioning", "scopes": ["orgs:write","pentests:write","usage:read"] }
// 201
{ "id": "key_123", "prefix": "mspp_live_a1b2", "secret": "mspp_live_a1b2c3…", "orgId": "org_distributor" }
GET /orgs/{id}/api-keys · DELETE /api-keys/{keyId}
List (prefix + metadata only) / revoke.
Pentests
POST /pentests
Launch. Runs a walk-up entitlement check (tier capability + pool/cap), reserves units, and enqueues the job.
// request
{ "tenantId": "org_client_x",
"scanType": "ai_pentest", // maps to a SKU
"targets": ["example.com", "10.0.0.0/24"],
"webhookUrl": "https://partner.example/hooks/mspp", // optional per-launch override
"metadata": { "poNumber": "PO-4471" } }
// 202 (queued)
{ "id": "pt_789", "status": "queued", "tenantId": "org_client_x",
"sku": "ai_pentest", "units": 2, "reserved": true, "createdAt": "2026-07-13T…Z" }
// 402 if hard-blocked
{ "error": { "code": "quota_exhausted", "message": "ai_pentest pool exhausted", "details": { "available": 0 } } }
GET /pentests?tenantId=&resellerId=&status= · GET /pentests/{id}
List (scoped to the key's subtree) / fetch status + findings summary.
GET /pentests/{id}/report?format=pdf|json
Branded report artifact (white-label cover applied from the org's resolved branding).
Usage & reporting
GET /orgs/{id}/usage?from=&to=&groupBy=sku|child|day
Rollup over the subtree from the append-only usage ledger — the same data consolidated invoices are computed from.
{ "from": "2026-07-01", "to": "2026-07-31",
"totals": { "ai_pentest": 30, "external": 12 },
"overage": { "ai_pentest": 0 },
"byChild": [ { "orgId": "org_acme", "ai_pentest": 18 } ] }
Outbound webhooks
Register endpoints and MSPP POSTs signed events (X-MSPP-Signature, HMAC-SHA256,
with retry).
POST /orgs/{id}/webhooks
{ "url": "https://partner.example/hooks/mspp",
"events": ["pentest.completed","report.ready","quota.soft_exceeded"] }
Event types: pentest.queued · pentest.completed · pentest.failed ·
report.ready · quota.soft_exceeded · quota.exhausted.
// example delivery
{ "type": "pentest.completed", "id": "evt_…", "createdAt": "…",
"data": { "pentestId": "pt_789", "tenantId": "org_client_x", "sku": "ai_pentest",
"findingsCount": 14, "reportUrl": "…" } }
Endpoint summary
| Method & path | Purpose |
|---|---|
POST /orgs · GET /orgs/{id} · GET /orgs/{id}/children · PATCH /orgs/{id} | Manage org tree |
PUT /orgs/{id}/quota · PUT /orgs/{id}/caps · GET /orgs/{id}/quota | Pools & caps |
POST /orgs/{id}/api-keys · GET … · DELETE /api-keys/{id} | Scoped keys |
POST /pentests · GET /pentests · GET /pentests/{id} · GET /pentests/{id}/report | Launch & results |
GET /orgs/{id}/usage | Consumption reporting / billing source |
POST /orgs/{id}/webhooks | Outbound event subscriptions |
Want partner access or an API key? Get in touch.