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.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
GET/customers/v1/partners/tenantsList all visible descendant tenants, including resellers and end customers.
POST/customers/v1/partners/tenantsCreate one child tenant under a parent organization.
POST/customers/v1/partners/tenants/bulkSubmit 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

  1. Create a child tenant with POST /customers/v1/partners/tenants, or submit a bulk job with POST /customers/v1/partners/tenants/bulk.
  2. For bulk jobs, poll GET /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.
  4. Use GET /customers/v1/partners/tenants to list the tenant hierarchy. Each listed tenantId matches the customerId returned by provisioning.

Idempotency

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

EndpointIdempotency behavior
POST /customers/v1/partners/tenantsOptional. Use the same key when retrying a single-tenant provisioning request.
POST /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 /customers/v1/partners/tenants/bulk/{jobId} directly. A fixed polling interval, such as every 30 seconds, is sufficient.


GET /customers/v1/partners/tenants

List 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

HeaderTypeRequiredDescription
AuthorizationstringYesDCI API JWT as Bearer <token>.

No request body or X-Idempotency-Key is required.

Query parameters

ParameterTypeRequiredDescription
maxResultsintegerNoPage size. Use a value from 1 to 500. Defaults to 50.
pageTokenstringNoOpaque 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
}
FieldTypeDescription
itemsarrayVisible descendant tenants in this page.
rowCountintegerNumber of items in this page, not the total across all pages.
pageTokenstringCursor for the next page. Omitted on the last page.

Each items entry has these fields:

FieldTypeAlways presentDescription
tenantIdstringYesDoiT customer ID of the tenant. This is the customerId returned by provisioning, not the provisioning response's tenantId.
namestringYesTenant display name.
primaryDomainstringYesTenant's primary domain; may be empty.
descriptionstringNoTenant description, when available.
tenantTypestringNoHierarchy role: distributor, reseller, or customer. End customers are returned as customer; provisioning uses endCustomer or end_customer.
levelintegerNoDepth in the hierarchy, when available: 0 for a root distributor, 1 for a reseller, and 2 for an end customer.
parentTenantIdstringNoDoiT 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

StatusDescription
400Invalid query parameters, such as a non-numeric maxResults or an invalid pageToken, or the calling organization could not be resolved.
401Valid authentication is required.
403The caller is not authorized to list partner tenants.
429Rate limit exceeded. Honor the Retry-After response header.
500The tenant hierarchy could not be retrieved or access scoping could not be applied.

POST /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://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

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 /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 /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://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

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 /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://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

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?