Partner Provisioning
Reference-style documentation for partner tenant listing, provisioning, bulk provisioning, and job polling endpoints.
Use partner endpoints to list the tenant hierarchy, create child tenants, and submit and monitor bulk provisioning jobs for partner-managed tenants.
Base URL
https://api.doit.comAuthentication
Send a DCI API JWT (API access token) in the Authorization header using Bearer authentication:
Authorization: Bearer <token>The calling tenant is taken from the JWT. Each endpoint also enforces authorization and tenant hierarchy rules.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /customers/v1/partners/tenants | List all visible descendant tenants, including resellers and end customers. |
POST | /customers/v1/partners/tenants | Create one child tenant under a parent organization. |
POST | /customers/v1/partners/tenants/bulk | Submit an asynchronous bulk provisioning job for up to 499 child tenants. |
GET | /customers/v1/partners/tenants/bulk/{jobId} | Poll a bulk provisioning job for progress and per-item results. |
Provisioning flow
- Create a child tenant with
POST /customers/v1/partners/tenants, or submit a bulk job withPOST /customers/v1/partners/tenants/bulk. - For bulk jobs, poll
GET /customers/v1/partners/tenants/bulk/{jobId}untilstatusis terminal:completed,partial, orfailed. - Read
tenantIdandcustomerIdfrom the single-tenant success response or from eachsucceededbulk item. - Use
GET /customers/v1/partners/tenantsto list the tenant hierarchy. Each listedtenantIdmatches thecustomerIdreturned by provisioning.
Idempotency
For retry-safe requests, send X-Idempotency-Key with the same value on retries.
| Endpoint | Idempotency behavior |
|---|---|
POST /customers/v1/partners/tenants | Optional. Use the same key when retrying a single-tenant provisioning request. |
POST /customers/v1/partners/tenants/bulk | Optional. Re-submitting the same key returns the original job response instead of creating a second job. |
Rate limits and polling
Bulk provisioning is limited to 10 bulk jobs per hour per token. When you receive 429, honor the Retry-After response header.
For bulk jobs, poll the statusUrl returned in the 202 response, or call GET /customers/v1/partners/tenants/bulk/{jobId} directly. A fixed polling interval, such as every 30 seconds, is sufficient.
GET /customers/v1/partners/tenants
GET /customers/v1/partners/tenantsList the tenant hierarchy below the calling organization. The response includes resellers and end customers at every depth, excluding the calling organization itself. The calling organization is resolved from the bearer token; this endpoint does not accept a parent tenant override.
Use the same API key or service account used for partner provisioning. Scoped API keys and service accounts require Tenant Provisioning (createRootTenant). When geographic or customer-group scoping applies, tenants outside the caller's effective scope are omitted.
Results are sorted by tenantId in ascending order and returned as a flat, paginated list. Use parentTenantId, tenantType, and level, when present, to reconstruct the visible hierarchy across all pages. A parent may be the calling organization or may be absent from the visible results because of scoping.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | DCI API JWT as Bearer <token>. |
No request body or X-Idempotency-Key is required.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
maxResults | integer | No | Page size. Use a value from 1 to 500. Defaults to 50. |
pageToken | string | No | Opaque cursor returned by the previous page. Omit for the first page. |
Example request
curl --get \
--url https://api.doit.com/customers/v1/partners/tenants \
--header 'Authorization: Bearer <token>' \
--data-urlencode 'maxResults=50'Success response
Returns 200 OK. This example shows a distributor's visible descendants: two end customers and their reseller.
{
"items": [
{
"tenantId": "cK4nM7pQ1sT0uV2wY5zA",
"name": "Acme Corp",
"primaryDomain": "acme.com",
"tenantType": "customer",
"level": 2,
"parentTenantId": "rS2vB8nQ4kL7xM1pW9tD"
},
{
"tenantId": "cP5nK8qM2sT1uV3wY7zB",
"name": "Beta Inc",
"primaryDomain": "beta.com",
"description": "Beta Inc EMEA",
"tenantType": "customer",
"level": 2,
"parentTenantId": "rS2vB8nQ4kL7xM1pW9tD"
},
{
"tenantId": "rS2vB8nQ4kL7xM1pW9tD",
"name": "Example Reseller",
"primaryDomain": "reseller.example.com",
"tenantType": "reseller",
"level": 1,
"parentTenantId": "dH6jL3mN9pQ2rT5vX8yB"
}
],
"rowCount": 3
}| Field | Type | Description |
|---|---|---|
items | array | Visible descendant tenants in this page. |
rowCount | integer | Number of items in this page, not the total across all pages. |
pageToken | string | Cursor for the next page. Omitted on the last page. |
Each items entry has these fields:
| Field | Type | Always present | Description |
|---|---|---|---|
tenantId | string | Yes | DoiT customer ID of the tenant. This is the customerId returned by provisioning, not the provisioning response's tenantId. |
name | string | Yes | Tenant display name. |
primaryDomain | string | Yes | Tenant's primary domain; may be empty. |
description | string | No | Tenant description, when available. |
tenantType | string | No | Hierarchy role: distributor, reseller, or customer. End customers are returned as customer; provisioning uses endCustomer or end_customer. |
level | integer | No | Depth in the hierarchy, when available: 0 for a root distributor, 1 for a reseller, and 2 for an end customer. |
parentTenantId | string | No | DoiT customer ID of the immediate parent tenant, when available. |
When there are no visible descendants, the response is {"items":[],"rowCount":0}.
Pagination
If a response includes pageToken, pass that value unchanged in the next request. URL-encode it, for example with curl's --data-urlencode, and keep the same maxResults value. Continue until pageToken is absent.
curl --get \
--url https://api.doit.com/customers/v1/partners/tenants \
--header 'Authorization: Bearer <token>' \
--data-urlencode 'maxResults=50' \
--data-urlencode 'pageToken=<pageToken from the previous response>'Error responses
| Status | Description |
|---|---|
400 | Invalid query parameters, such as a non-numeric maxResults or an invalid pageToken, or the calling organization could not be resolved. |
401 | Valid authentication is required. |
403 | The caller is not authorized to list partner tenants. |
429 | Rate limit exceeded. Honor the Retry-After response header. |
500 | The tenant hierarchy could not be retrieved or access scoping could not be applied. |
POST /customers/v1/partners/tenants
POST /customers/v1/partners/tenantsCreate one child tenant under a parent organization.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | DCI API JWT as Bearer <token>. |
X-Idempotency-Key | string | No | Optional idempotency key for retry-safe provisioning. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
tenantName | string | Yes | Display name for the new tenant. Maximum 200 characters. |
adminEmail | string | Yes | Admin user email address. |
parentTenantId | string | No | DoiT customer ID of the parent tenant. When omitted, empty, or whitespace-only, the calling organization from the JWT is used as the parent. |
primaryDomain | string or null | No | Primary DNS domain for the tenant. Omit, send null, or use an empty string for no domain. The same primary domain may be used by multiple tenants. |
tenantType | string | No | Explicit hierarchy role. Accepted values: endCustomer, reseller. |
subscriptionId | string | No | Optional partner subscription or line identifier. |
adminFirstName | string | No | Tenant admin given name. Maximum 200 characters. |
adminLastName | string | No | Tenant admin family name. Maximum 200 characters. |
adminJobFunction | string | No | Tenant admin job function. Send one of the documented enum labels verbatim. |
externalId | string | No | Partner-assigned identifier for this tenant. Maximum 255 characters. |
countryCode | string | No | ISO 3166-1 alpha-2 country code, such as US, DE, or JP. |
Example request
curl --request POST \
--url https://api.doit.com/customers/v1/partners/tenants \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Idempotency-Key: tenant-create-001' \
--data '{
"parentTenantId": "mR3cK9vL2nP8qW5yT7hJ",
"tenantName": "Acme Corp",
"primaryDomain": "acme.com",
"tenantType": "endCustomer",
"adminEmail": "[email protected]",
"adminFirstName": "Jane",
"adminLastName": "Doe",
"adminJobFunction": "Software / Ops Engineer",
"externalId": "BCN-12345",
"countryCode": "US"
}'Success response
Returns 201 Created when the child tenant is provisioned.
{
"tenantId": "tN8kP2mQ5vL9wX3yR6jH",
"customerId": "cK4nM7pQ1sT0uV2wY5zA",
"message": "Tenant provisioned successfully",
"adminEmail": "[email protected]"
}Error responses
| Status | Description |
|---|---|
400 | Request validation failed. |
401 | Valid authentication is required. |
403 | The calling organization is not authorized for this operation. |
404 | The requested resource was not found or is not visible to the caller. |
409 | Idempotency replay. The original tenant identifiers are returned when the original request succeeded. |
429 | Rate limit exceeded. Honor the Retry-After response header. |
503 | Idempotency lock in progress. Retry shortly. |
POST /customers/v1/partners/tenants/bulk
POST /customers/v1/partners/tenants/bulkSubmit an asynchronous job to provision up to 499 child tenants.
Items are processed independently, so a failure on one item does not block the others. Poll the returned statusUrl or call GET /customers/v1/partners/tenants/bulk/{jobId} until the job reaches a terminal status.
If an item omits tier, the job-level defaultTier applies. If both are omitted, the platform resolves the tier from the calling organization's hierarchy. If an item omits parentTenantId, the job-level defaultParentTenantId applies. If both are omitted, the calling organization from the JWT is used as the parent.
An item is considered a duplicate only when parentTenantId, tenantName, and adminEmail match a previously provisioned item. primaryDomain is not part of duplicate detection. Duplicate entries are automatically skipped.
Jobs of up to 499 tenants typically reach a terminal state within about 50 minutes, based on approximately 1 minute per 10 tenants. This is a sizing guideline for polling, not an SLA.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | DCI API JWT as Bearer <token>. |
X-Idempotency-Key | string | No | Optional idempotency key. UUID v4 or ULID recommended. Maximum 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
tenants | array | Yes | Tenant provisioning items. Must contain 1 to 499 entries. |
defaultParentTenantId | string | No | Default parent tenant ID applied to items that omit parentTenantId. |
defaultTier | string | No | Default tier applied to items that omit tier. Accepted values: end_customer, reseller. |
Each tenants item supports these fields:
| Field | Type | Required | Description |
|---|---|---|---|
tenantName | string | Yes | Display name for the tenant. Maximum 200 characters. |
adminEmail | string | Yes | Admin user email address. |
primaryDomain | string or null | No | Primary DNS domain for the tenant. The same domain may be used by multiple tenants and is not part of duplicate detection. |
adminFirstName | string | No | Tenant admin given name. Maximum 200 characters. |
adminLastName | string | No | Tenant admin family name. Maximum 200 characters. |
adminJobFunction | string | No | Tenant admin job function. Send one of the documented enum labels verbatim. |
parentTenantId | string | No | Per-item parent tenant override. |
tier | string | No | Per-item tier override. Accepted values: end_customer, reseller. |
externalId | string | No | Partner-assigned identifier for this tenant. Maximum 255 characters. |
countryCode | string | No | ISO 3166-1 alpha-2 country code. |
Example request
curl --request POST \
--url https://api.doit.com/customers/v1/partners/tenants/bulk \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Idempotency-Key: bulk-job-001' \
--data '{
"defaultParentTenantId": "mR3cK9vL2nP8qW5yT7hJ",
"defaultTier": "reseller",
"tenants": [
{
"tenantName": "Acme Corp",
"primaryDomain": "acme.com",
"adminEmail": "[email protected]",
"adminFirstName": "Jane",
"adminLastName": "Doe",
"adminJobFunction": "Software / Ops Engineer"
},
{
"tenantName": "Beta Inc",
"tier": "end_customer",
"adminEmail": "[email protected]",
"adminFirstName": "John",
"adminLastName": "Smith"
}
]
}'Success response
Returns 202 Accepted when the job is queued for asynchronous processing.
{
"jobId": "job_01HZXXXXXXXXXXXXXXXXXXXXXX",
"statusUrl": "https://api.doit.com/customers/v1/partners/tenants/bulk/job_01HZXXXXXXXXXXXXXXXXXXXXXX",
"message": "Bulk provisioning job accepted"
}Error responses
| Status | Description |
|---|---|
400 | Validation failed. Check details for field-level errors. |
401 | Valid authentication is required. |
403 | The calling organization is not authorized for bulk provisioning. |
409 | Idempotency replay. The original job ID and statusUrl are returned. |
429 | Bulk job rate limit exceeded. Honor the Retry-After response header. |
GET /customers/v1/partners/tenants/bulk/{jobId}
GET /customers/v1/partners/tenants/bulk/{jobId}Get the current status and per-item results for a bulk provisioning job.
Poll until status is terminal: completed, partial, or failed. The items array returns one result per input item in original order.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
jobId | string | Yes | Bulk provisioning job ID returned by the submit endpoint. |
Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | DCI API JWT as Bearer <token>. |
Example request
curl --request GET \
--url https://api.doit.com/customers/v1/partners/tenants/bulk/job_01HZXXXXXXXXXXXXXXXXXXXXXX \
--header 'Authorization: Bearer <token>'Success response: processing
{
"jobId": "job_01HZXXXXXXXXXXXXXXXXXXXXXX",
"status": "processing",
"total": 3,
"succeeded": 1,
"failed": 0,
"skipped": 0,
"pending": 2,
"submittedAt": "2024-06-01T12:00:00Z",
"statusAt": "2024-06-01T12:00:42Z",
"items": [
{
"index": 0,
"tenantName": "Acme Corp",
"status": "succeeded",
"tenantId": "tN8kP2mQ5vL9wX3yR6jH",
"customerId": "cK4nM7pQ1sT0uV2wY5zA",
"adminEmail": "[email protected]"
},
{
"index": 1,
"tenantName": "Beta Inc",
"status": "processing"
},
{
"index": 2,
"tenantName": "Gamma Ltd",
"status": "pending"
}
]
}Success response: partial
{
"jobId": "job_01HZXXXXXXXXXXXXXXXXXXXXXX",
"status": "partial",
"total": 3,
"succeeded": 2,
"failed": 1,
"skipped": 0,
"pending": 0,
"submittedAt": "2024-06-01T12:00:00Z",
"statusAt": "2024-06-01T12:02:15Z",
"items": [
{
"index": 0,
"tenantName": "Acme Corp",
"status": "succeeded",
"tenantId": "tN8kP2mQ5vL9wX3yR6jH",
"customerId": "cK4nM7pQ1sT0uV2wY5zA",
"adminEmail": "[email protected]"
},
{
"index": 1,
"tenantName": "Beta Inc",
"status": "failed",
"errorCode": "validation_failed",
"errorMessage": "The item failed pre-provisioning validation"
},
{
"index": 2,
"tenantName": "Gamma Ltd",
"status": "skipped",
"skipReason": "duplicate_tenant",
"conflictJobId": "job_01HZYYYYYYYYYYYYYYYYYYYYYY"
}
]
}Job statuses
| Status | Description |
|---|---|
pending | The job was accepted but has not started. |
processing | At least one item is being processed. |
completed | The job finished and every item succeeded. |
partial | The job finished with mixed outcomes, including skipped or failed items. |
failed | The job finished and every item failed. |
Item statuses
| Status | Description |
|---|---|
pending | The item was accepted but has not started. |
processing | The item is being processed. |
succeeded | The tenant was created successfully. Read tenantId and customerId from the item. |
failed | Provisioning failed. Read errorCode and errorMessage from the item. |
skipped | The item was deduplicated. Read skipReason and, when present, conflictJobId. duplicate_tenant means a previously provisioned item has the same parentTenantId, tenantName, and adminEmail; primaryDomain is not considered. |
Error responses
| Status | Description |
|---|---|
401 | Valid authentication is required. |
403 | The caller is not authorized to view the job. |
404 | The job was not found or is not visible to the caller. |
Job function values
Use one of these values for adminJobFunction when you include it:
Data Engineer / Data AnalystsExecutive TeamFinance / AccountingFounderLegal / PurchasingManagementSales / MarketingSoftware / Ops Engineer
Updated 15 days ago