API Reference
Integrate baref00t.io into your stack. Manage customers, trigger assessments, and automate security workflows through the Partner and Distributor APIs.
Overview
baref00t.io exposes two REST APIs that let you automate security assessment workflows. The Partner API is for MSPs who manage their own customers and run assessments against those tenants. The Distributor API is for organisations that provision and manage multiple partner accounts underneath a single billing relationship.
Both APIs accept and return JSON. All requests must be made over HTTPS. HTTP requests are rejected.
Authentication
Every request must include an API key in a custom header. Partner keys and Distributor keys use separate headers and separate prefixes so the server can route your request to the correct API surface.
Partner API Key
Pass your key in the X-Partner-Key header. Partner keys always start with pk_live_.
curl https://api.baref00t.io/api/v1/partner/me \ -H "X-Partner-Key: pk_live_abc123def456"
Distributor API Key
Pass your key in the X-Distributor-Key header. Distributor keys always start with dk_live_.
curl https://api.baref00t.io/api/v1/distributor/me \ -H "X-Distributor-Key: dk_live_xyz789ghi012"
Key Management
Each partner account can hold a maximum of two active API keys at any time. Use the Generate API Key and Revoke API Key endpoints to rotate keys with zero downtime: generate a new key in slot 2 while slot 1 is still active, migrate your systems, then revoke slot 1.
Base URLs
| API | Base URL |
|---|---|
| Partner | https://api.baref00t.io/api/v1/partner/ |
| Distributor | https://api.baref00t.io/api/v1/distributor/ |
All endpoint paths in this reference are shown relative to the base URL. For example, GET /v1/partner/me means you send the request to https://api.baref00t.io/api/v1/partner/me.
Rate Limits
| API | Limit | Window |
|---|---|---|
| Partner | 60 requests | per minute, per key |
| Distributor | 120 requests | per minute, per key |
When you exceed the limit, the API returns a 429 status with the error code RATE_LIMITED. The response includes a Retry-After header indicating how many seconds to wait before retrying.
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 12 seconds."
}
}Error Handling
All error responses follow a consistent envelope. The HTTP status code indicates the error category, and the response body contains a machine-readable code and a human-readable message.
{
"error": {
"code": "ERROR_CODE",
"message": "Human readable description of what went wrong."
}
}Error Codes
| Code | Status | Description |
|---|---|---|
| MISSING_KEY | 401 | No API key header was provided in the request. |
| INVALID_KEY | 401 | The API key is malformed, expired, or revoked. |
| RATE_LIMITED | 429 | You have exceeded the rate limit for your API tier. |
| PARTNER_INACTIVE | 403 | The partner account is suspended or pending activation. |
| DISTRIBUTOR_INACTIVE | 403 | The distributor account is suspended or pending activation. |
| INVALID_JSON | 400 | The request body is not valid JSON. |
| MISSING_FIELD | 400 | A required field is missing from the request body. |
| INVALID_PRODUCT | 400 | The product identifier is not recognised. |
| PRODUCT_NOT_ALLOWED | 403 | Your plan does not include the requested product. |
| RUN_LIMIT_REACHED | 403 | You have reached the monthly assessment run limit for your plan. |
| NOT_FOUND | 404 | The requested resource does not exist or you do not have access. |
| MAX_KEYS | 409 | You already have the maximum number of API keys (2). |
| TOO_MANY | 400 | The bulk request exceeds the maximum batch size. |
Webhooks
Configure a webhook URL in your partner or distributor portal to receive real-time event notifications. Webhooks are delivered as POST requests with a JSON body to the URL you specify.
Events
assessment.startedAn assessment run has been queued and is beginning execution.assessment.completedAn assessment run has finished and results are available.assessment.failedAn assessment run encountered an error and could not complete.customer.createdA new customer record was created.customer.updatedA customer record was modified.customer.deletedA customer record was removed.partner.activatedA sub-partner account has been activated (Distributor only).partner.suspendedA sub-partner account has been suspended (Distributor only).key.createdA new API key was generated for the account.key.revokedAn API key was revoked.
Payload Format
{
"id": "evt_a1b2c3d4e5f6",
"type": "assessment.completed",
"timestamp": "2026-04-03T08:15:22Z",
"data": {
"assessmentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"product": "essential-eight",
"status": "completed",
"score": 72
}
}Signature Verification
Every webhook request includes a X-Baref00t-Signature header containing an HMAC-SHA256 signature of the raw request body. Verify this signature using the webhook secret displayed in your portal to ensure the request originated from baref00t.io.
// Pseudocode: verify webhook signature expected = HMAC-SHA256(webhook_secret, raw_request_body) received = request.headers["X-Baref00t-Signature"] // Use a constant-time comparison to prevent timing attacks valid = constant_time_equal(expected, received)
Delivery
Webhooks are delivered with a timeout of 10 seconds. If your endpoint returns a non-2xx status code or times out, the delivery is retried up to 5 times with exponential backoff (1 min, 5 min, 30 min, 2 hr, 12 hr). After all retries are exhausted, the event is marked as failed in your portal's webhook log.
200 status code as quickly as possible. Process the event payload asynchronously to avoid timeouts.Partner API
The Partner API lets MSPs manage customers and run security assessments through a single API key. All endpoints require the X-Partner-Key header.
Response 200
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"company": "Acme MSP",
"email": "admin@acmemsp.com",
"plan": "professional",
"status": "active",
"usage": {
"month": "2026-04",
"runsUsed": 42,
"runsLimit": 200,
"customersCount": 18
},
"createdAt": "2026-01-15T09:30:00Z"
}Request body
{
"plan": "professional", // required: 'starter' | 'professional' | 'enterprise'
"billing": "monthly", // optional: 'monthly' | 'rolling' (annual). Defaults to current.
"currency": "usd" // optional: 'usd' | 'aud' | 'gbp' | 'eur' | 'sgd'. Auto-detected from your current subscription if omitted.
}Response 200
{
"partnerId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"plan": "professional",
"billing": "monthly",
"currency": "usd",
"runLimit": 100,
"subscriptionStatus": "active",
"currentPeriodEnd": "2026-05-09T00:00:00Z"
}Errors
400 INVALID_JSON — request body could not be parsed
400 PLAN_CHANGE_FAILED — invalid plan/billing, no active subscription, already on target plan,
or subscription is in a state that cannot be modified (canceled, etc)
401 Missing or invalid X-Partner-Key
500 Price lookup or Stripe API errorNotes
Rate-limited to 10 plan changes per hour per partner. Upgrades/downgrades are audited. Dashboard UI is available on the Billing tab of the partner portal.
Response 200
{
"products": [
{
"id": "essential-eight",
"name": "Essential Eight Assessment",
"category": "compliance",
"estimatedDuration": 180
},
{
"id": "nist-csf",
"name": "NIST CSF Assessment",
"category": "compliance",
"estimatedDuration": 240
}
]
}Response 200
{
"customers": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"createdAt": "2026-02-10T14:00:00Z"
}
]
}Request Body
| Field | Type | Description | |
|---|---|---|---|
| name | string | required | Display name for the customer organisation. |
| tenantId | string (GUID) | required | Microsoft 365 tenant ID for the customer. |
| string | optional | Primary contact email address. | |
| receivers | string[] | optional | Email addresses that receive assessment report notifications. |
| questionnaireRecipients | string[] | optional | Email addresses that receive questionnaire invitations. |
{
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"receivers": ["ciso@contoso.com", "it@contoso.com"],
"questionnaireRecipients": ["ciso@contoso.com"]
}Response 201
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"receivers": ["ciso@contoso.com", "it@contoso.com"],
"questionnaireRecipients": ["ciso@contoso.com"],
"createdAt": "2026-04-03T10:00:00Z"
}curl Example
curl -X POST https://api.baref00t.io/api/v1/partner/customers \
-H "X-Partner-Key: pk_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"receivers": ["ciso@contoso.com"],
"questionnaireRecipients": ["ciso@contoso.com"]
}'Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Customer ID (e.g. b2c3d4e5-f6a7-8901-bcde-f12345678901). |
Response 200
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"receivers": ["ciso@contoso.com", "it@contoso.com"],
"questionnaireRecipients": ["ciso@contoso.com"],
"recentRuns": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"product": "essential-eight",
"status": "completed",
"completedAt": "2026-03-28T14:22:00Z"
}
],
"createdAt": "2026-02-10T14:00:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Customer ID. |
Request Body
{
"name": "Contoso Corporation",
"receivers": ["ciso@contoso.com", "security@contoso.com"]
}Response 200
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Contoso Corporation",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"receivers": ["ciso@contoso.com", "security@contoso.com"],
"questionnaireRecipients": ["ciso@contoso.com"],
"updatedAt": "2026-04-03T10:05:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Customer ID. |
Response 204
No content. The customer has been deleted.
Request Body
| Field | Type | Description | |
|---|---|---|---|
| customerId | string | required | The customer to run the assessment against. |
| product | string | required | Product identifier (e.g. essential-eight, nist-csf). |
| maturityTarget | number | optional | Target maturity level (1-5). Defaults to the product default. |
{
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"product": "essential-eight",
"maturityTarget": 3
}Response 202
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"product": "essential-eight",
"maturityTarget": 3,
"status": "queued",
"createdAt": "2026-04-03T10:10:00Z"
}curl Example
curl -X POST https://api.baref00t.io/api/v1/partner/assessments \
-H "X-Partner-Key: pk_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"product": "essential-eight",
"maturityTarget": 3
}'Query Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| month | string | optional | Filter by month in YYYY-MM format. |
| product | string | optional | Filter by product identifier. |
| customerId | string | optional | Filter by customer ID. |
Response 200
{
"runs": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"product": "essential-eight",
"status": "completed",
"score": 72,
"createdAt": "2026-04-03T10:10:00Z",
"completedAt": "2026-04-03T10:13:22Z"
}
]
}curl Example
curl "https://api.baref00t.io/api/v1/partner/assessments?month=2026-04&product=essential-eight" \ -H "X-Partner-Key: pk_live_abc123def456"
Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Assessment run ID (e.g. a1b2c3d4-e5f6-7890-abcd-ef1234567890). |
Response 200
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"customerName": "Contoso Ltd",
"product": "essential-eight",
"maturityTarget": 3,
"status": "completed",
"score": 72,
"findings": 14,
"critical": 2,
"high": 5,
"medium": 4,
"low": 3,
"reportUrl": "https://api.baref00t.io/api/r/{token}",
"createdAt": "2026-04-03T10:10:00Z",
"completedAt": "2026-04-03T10:13:22Z"
}Request Body
{
"customers": [
{
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com"
},
{
"name": "Fabrikam Inc",
"tenantId": "a1b2c3d4-5678-9abc-def0-123456789abc",
"email": "admin@fabrikam.com"
}
]
}Response 201
{
"created": 2,
"failed": 0,
"customers": [
{ "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "name": "Contoso Ltd", "status": "created" },
{ "id": "d4e5f6a7-b8c9-0123-defa-234567890123", "name": "Fabrikam Inc", "status": "created" }
]
}TOO_MANY error.Request Body
| Field | Type | Description | |
|---|---|---|---|
| product | string | required | Product identifier to run. |
| customerIds | string[] | required | Array of customer IDs. Max 50. |
| maturityTarget | number | optional | Target maturity level applied to all runs. |
{
"product": "essential-eight",
"customerIds": ["b2c3d4e5-f6a7-8901-bcde-f12345678901", "d4e5f6a7-b8c9-0123-defa-234567890123", "e5f6a7b8-c9d0-1234-efab-345678901234"],
"maturityTarget": 3
}Response 202
{
"queued": 3,
"failed": 0,
"runs": [
{ "id": "f6a7b8c9-d0e1-2345-abcd-456789012345", "customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "status": "queued" },
{ "id": "a7b8c9d0-e1f2-3456-bcde-567890123456", "customerId": "d4e5f6a7-b8c9-0123-defa-234567890123", "status": "queued" },
{ "id": "b8c9d0e1-f2a3-4567-cdef-678901234567", "customerId": "e5f6a7b8-c9d0-1234-efab-345678901234", "status": "queued" }
]
}TOO_MANY error.Request Body
No request body required.
Response 201
{
"slot": 2,
"key": "pk_live_newkey789xyz",
"createdAt": "2026-04-03T10:20:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| slot | number | required | Key slot to revoke: 1 or 2. |
Response 204
No content. The key has been revoked.
Distributor API
The Distributor API lets you provision and manage multiple partner accounts under a single billing relationship. All endpoints require the X-Distributor-Key header.
Response 200
{
"id": "dist_xyz789",
"company": "Global Security Distribution",
"email": "ops@globalsecdist.com",
"status": "active",
"partnersCount": 12,
"usage": {
"month": "2026-04",
"totalRuns": 384,
"totalCustomers": 156
},
"createdAt": "2025-11-01T08:00:00Z"
}Query Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| status | string | optional | Filter by status: active, suspended, pending. |
Response 200
{
"partners": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"company": "Acme MSP",
"email": "admin@acmemsp.com",
"plan": "professional",
"status": "active",
"customersCount": 18,
"createdAt": "2026-01-15T09:30:00Z"
}
]
}Request Body
| Field | Type | Description | |
|---|---|---|---|
| string | required | Admin email for the new partner account. | |
| company | string | required | Company name for the partner. |
| name | string | required | Contact name for the partner admin. |
| plan | string | required | Plan to assign: starter, professional, enterprise. |
| billing | object | required | Billing configuration for the partner. |
| billing.currency | string | required | ISO 4217 currency code: AUD, USD, GBP, EUR, SGD. |
| billing.interval | string | optional | Billing interval: monthly (default), quarterly, or annual. |
{
"email": "admin@newmsp.com",
"company": "New MSP Pty Ltd",
"name": "Jane Smith",
"plan": "professional",
"billing": {
"currency": "AUD",
"interval": "monthly"
}
}Response 201
{
"id": "prt_new456",
"company": "New MSP Pty Ltd",
"email": "admin@newmsp.com",
"plan": "professional",
"status": "pending",
"billing": {
"currency": "AUD",
"interval": "monthly"
},
"createdAt": "2026-04-03T11:00:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID (e.g. c3d4e5f6-a7b8-9012-cdef-123456789012). |
Response 200
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"company": "Acme MSP",
"email": "admin@acmemsp.com",
"name": "John Doe",
"plan": "professional",
"status": "active",
"billing": {
"currency": "AUD",
"interval": "monthly"
},
"usage": {
"month": "2026-04",
"runsUsed": 42,
"runsLimit": 200,
"customersCount": 18
},
"createdAt": "2026-01-15T09:30:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID. |
Request Body
{
"plan": "enterprise",
"billing": {
"interval": "annual"
}
}Response 200
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"plan": "enterprise",
"billing": {
"currency": "AUD",
"interval": "annual"
},
"updatedAt": "2026-04-03T11:15:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID. |
Request Body
No request body required.
Response 200
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"status": "active",
"activatedAt": "2026-04-03T11:20:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID. |
Request Body
No request body required.
Response 200
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"status": "suspended",
"suspendedAt": "2026-04-03T11:25:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID (must belong to this distributor). |
Request body
{
"plan": "enterprise", // required: 'starter' | 'professional' | 'enterprise'
"billing": "rolling", // optional: 'monthly' | 'rolling' (annual). Defaults to partner's current.
"currency": "aud" // optional: 'usd' | 'aud' | 'gbp' | 'eur' | 'sgd'. Auto-detected from partner's current subscription if omitted.
}Response 200
{
"partnerId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"plan": "enterprise",
"billing": "rolling",
"currency": "aud",
"runLimit": -1, // -1 = unlimited
"subscriptionStatus": "active",
"currentPeriodEnd": "2027-04-09T00:00:00Z"
}Errors
404 NOT_FOUND — partner does not exist or is not owned by this distributor
400 PLAN_CHANGE_FAILED — invalid plan/billing, no active subscription, already on target plan,
or subscription is in a state that cannot be modified
401 Missing or invalid X-Distributor-Key
500 Price lookup or Stripe API errorNotes
Plan changes against sub-partners are audited under the distributor's ID. If the distributor has StripeBillingEnabled=false, the partner's own Stripe subscription is still updated — revenue-share settlement is tracked separately.
Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID. |
Response 200
{
"customers": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"createdAt": "2026-02-10T14:00:00Z"
}
]
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID to create the customer under. |
Request Body
{
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"receivers": ["ciso@contoso.com"],
"questionnaireRecipients": ["ciso@contoso.com"]
}Response 201
{
"id": "cust_new789",
"name": "Contoso Ltd",
"tenantId": "d4f5a678-1234-5678-9abc-def012345678",
"email": "it@contoso.com",
"receivers": ["ciso@contoso.com"],
"questionnaireRecipients": ["ciso@contoso.com"],
"partnerId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"createdAt": "2026-04-03T11:30:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID. |
Request Body
{
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"product": "essential-eight",
"maturityTarget": 3
}Response 202
{
"id": "c9d0e1f2-a3b4-5678-defa-789012345678",
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"partnerId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"product": "essential-eight",
"status": "queued",
"createdAt": "2026-04-03T11:35:00Z"
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| id | string | required | Partner ID. |
Response 200
{
"runs": [
{
"id": "c9d0e1f2-a3b4-5678-defa-789012345678",
"customerId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"product": "essential-eight",
"status": "completed",
"score": 72,
"createdAt": "2026-04-03T11:35:00Z",
"completedAt": "2026-04-03T11:38:44Z"
}
]
}Query Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| month | string | optional | Month in YYYY-MM format. Defaults to current month. |
Response 200
{
"month": "2026-04",
"totalPartners": 12,
"activePartners": 10,
"totalCustomers": 156,
"totalRuns": 384,
"byPartner": [
{
"partnerId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"company": "Acme MSP",
"runs": 42,
"customers": 18
},
{
"partnerId": "prt_def456",
"company": "SecureIT Partners",
"runs": 67,
"customers": 24
}
]
}Path Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| slot | string | required | Key slot to revoke: 1 or 2. |
Query Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| distributorId | string | required | UUID of the distributor whose key to revoke. |
Response 200
{
"revoked": true,
"slot": 1
}Request Body
{
"partners": [
{
"email": "admin@mspalpha.com",
"company": "MSP Alpha",
"name": "Alice Wong",
"plan": "professional",
"billing": { "currency": "AUD", "interval": "monthly" }
},
{
"email": "admin@mspbeta.com",
"company": "MSP Beta",
"name": "Bob Chen",
"plan": "starter",
"billing": { "currency": "USD", "interval": "monthly" }
}
]
}Response 201
{
"created": 2,
"failed": 0,
"partners": [
{ "id": "prt_alpha01", "company": "MSP Alpha", "status": "pending" },
{ "id": "prt_beta02", "company": "MSP Beta", "status": "pending" }
]
}TOO_MANY error.