Partner Provisioning
Reference-style documentation for partner tenant provisioning, bulk provisioning, and job polling endpoints.
Use partner provisioning endpoints to create child tenants and submit and monitor bulk provisioning jobs for partner-managed tenants.
Base URL
https://console.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 |
|---|---|---|
POST | /api/customers/v1/partners/tenants | Create one child tenant under a parent organization. |
POST | /api/customers/v1/partners/tenants/bulk | Submit an asynchronous bulk provisioning job for up to 499 child tenants. |
GET | /api/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 /api/customers/v1/partners/tenants, or submit a bulk job withPOST /api/customers/v1/partners/tenants/bulk. - For bulk jobs, poll
GET /api/customers/v1/partners/tenants/bulk/{jobId}untilstatusis terminal:completed,partial, orfailed. - Read
tenantIdandcustomerIdfrom the single-tenant success response or from eachsucceededbulk item.
Idempotency
For retry-safe requests, send X-Idempotency-Key with the same value on retries.
| Endpoint | Idempotency behavior |
|---|---|
POST /api/customers/v1/partners/tenants | Optional. Use the same key when retrying a single-tenant provisioning request. |
POST /api/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 /api/customers/v1/partners/tenants/bulk/{jobId} directly. A fixed polling interval, such as every 30 seconds, is sufficient.
POST /api/customers/v1/partners/tenants
POST /api/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://console.doit.com/api/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 /api/customers/v1/partners/tenants/bulk
POST /api/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 /api/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://console.doit.com/api/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://console.doit.com/api/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 /api/customers/v1/partners/tenants/bulk/{jobId}
GET /api/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://console.doit.com/api/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 5 days ago