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.com

Authentication

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

MethodPathDescription
POST/api/customers/v1/partners/tenantsCreate one child tenant under a parent organization.
POST/api/customers/v1/partners/tenants/bulkSubmit 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

  1. Create a child tenant with POST /api/customers/v1/partners/tenants, or submit a bulk job with POST /api/customers/v1/partners/tenants/bulk.
  2. For bulk jobs, poll GET /api/customers/v1/partners/tenants/bulk/{jobId} until status is terminal: completed, partial, or failed.
  3. Read tenantId and customerId from the single-tenant success response or from each succeeded bulk item.

Idempotency

For retry-safe requests, send X-Idempotency-Key with the same value on retries.

EndpointIdempotency behavior
POST /api/customers/v1/partners/tenantsOptional. Use the same key when retrying a single-tenant provisioning request.
POST /api/customers/v1/partners/tenants/bulkOptional. 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

Create one child tenant under a parent organization.

Headers

HeaderTypeRequiredDescription
AuthorizationstringYesDCI API JWT as Bearer <token>.
X-Idempotency-KeystringNoOptional idempotency key for retry-safe provisioning.

Request body

FieldTypeRequiredDescription
tenantNamestringYesDisplay name for the new tenant. Maximum 200 characters.
adminEmailstringYesAdmin user email address.
parentTenantIdstringNoDoiT customer ID of the parent tenant. When omitted, empty, or whitespace-only, the calling organization from the JWT is used as the parent.
primaryDomainstring or nullNoPrimary 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.
tenantTypestringNoExplicit hierarchy role. Accepted values: endCustomer, reseller.
subscriptionIdstringNoOptional partner subscription or line identifier.
adminFirstNamestringNoTenant admin given name. Maximum 200 characters.
adminLastNamestringNoTenant admin family name. Maximum 200 characters.
adminJobFunctionstringNoTenant admin job function. Send one of the documented enum labels verbatim.
externalIdstringNoPartner-assigned identifier for this tenant. Maximum 255 characters.
countryCodestringNoISO 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

StatusDescription
400Request validation failed.
401Valid authentication is required.
403The calling organization is not authorized for this operation.
404The requested resource was not found or is not visible to the caller.
409Idempotency replay. The original tenant identifiers are returned when the original request succeeded.
429Rate limit exceeded. Honor the Retry-After response header.
503Idempotency lock in progress. Retry shortly.

POST /api/customers/v1/partners/tenants/bulk

Submit 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

HeaderTypeRequiredDescription
AuthorizationstringYesDCI API JWT as Bearer <token>.
X-Idempotency-KeystringNoOptional idempotency key. UUID v4 or ULID recommended. Maximum 255 characters.

Request body

FieldTypeRequiredDescription
tenantsarrayYesTenant provisioning items. Must contain 1 to 499 entries.
defaultParentTenantIdstringNoDefault parent tenant ID applied to items that omit parentTenantId.
defaultTierstringNoDefault tier applied to items that omit tier. Accepted values: end_customer, reseller.

Each tenants item supports these fields:

FieldTypeRequiredDescription
tenantNamestringYesDisplay name for the tenant. Maximum 200 characters.
adminEmailstringYesAdmin user email address.
primaryDomainstring or nullNoPrimary DNS domain for the tenant. The same domain may be used by multiple tenants and is not part of duplicate detection.
adminFirstNamestringNoTenant admin given name. Maximum 200 characters.
adminLastNamestringNoTenant admin family name. Maximum 200 characters.
adminJobFunctionstringNoTenant admin job function. Send one of the documented enum labels verbatim.
parentTenantIdstringNoPer-item parent tenant override.
tierstringNoPer-item tier override. Accepted values: end_customer, reseller.
externalIdstringNoPartner-assigned identifier for this tenant. Maximum 255 characters.
countryCodestringNoISO 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

StatusDescription
400Validation failed. Check details for field-level errors.
401Valid authentication is required.
403The calling organization is not authorized for bulk provisioning.
409Idempotency replay. The original job ID and statusUrl are returned.
429Bulk job rate limit exceeded. Honor the Retry-After response header.

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

ParameterTypeRequiredDescription
jobIdstringYesBulk provisioning job ID returned by the submit endpoint.

Headers

HeaderTypeRequiredDescription
AuthorizationstringYesDCI 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

StatusDescription
pendingThe job was accepted but has not started.
processingAt least one item is being processed.
completedThe job finished and every item succeeded.
partialThe job finished with mixed outcomes, including skipped or failed items.
failedThe job finished and every item failed.

Item statuses

StatusDescription
pendingThe item was accepted but has not started.
processingThe item is being processed.
succeededThe tenant was created successfully. Read tenantId and customerId from the item.
failedProvisioning failed. Read errorCode and errorMessage from the item.
skippedThe 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

StatusDescription
401Valid authentication is required.
403The caller is not authorized to view the job.
404The 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 Analysts
  • Executive Team
  • Finance / Accounting
  • Founder
  • Legal / Purchasing
  • Management
  • Sales / Marketing
  • Software / Ops Engineer

Did this page help you?