AppDirect Tenant Marketplace Provisioning API
Tenant-owner M2M APIs for marketplace partners (org create, subscription lifecycle, balance top-up, member seats). Requires a tenant-scoped API key from the tenant-owner org.
Tenant-owner M2M APIs for marketplace partners (org create, subscription lifecycle, balance top-up, member seats). Requires a tenant-scoped API key from the tenant-owner org.
openapi: 3.0.0
info:
description: The Companies API allows developers to manage marketplace companies and their user memberships.
title: Companies AI Embed Tenant Marketplace Provisioning API
license:
name: Apache License, Version 2.0
url: http://www.apache.org/licenses/LICENSE-2.0
version: v296.0-SNAPSHOT
servers:
- url: https://marketplace.appdirect.com/api
- url: https://virtserver.swaggerhub.com
tags:
- name: Tenant Marketplace Provisioning
description: Tenant-owner M2M APIs for marketplace partners (org create, subscription lifecycle, balance top-up, member seats). Requires a tenant-scoped API key from the tenant-owner org.
paths:
/api/v1/tenant/organizations/create:
post:
tags:
- Tenant Marketplace Provisioning
summary: Create tenant organization
description: Creates a child organization under the tenant and returns the org ID and resolved admin user ID. When the tenant has subscription billing management enabled, the org is created without an automatic paid subscription (FREE bootstrap) so step 2 is POST .../subscription. Requires ORG_SETTINGS WRITE with a tenant-scoped API key from the tenant-owner org.
operationId: createTenantOrganization
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTenantOrganizationRequestDto'
responses:
'201':
description: Organization created.
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTenantOrganizationResponseDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/subscription:
get:
tags:
- Tenant Marketplace Provisioning
summary: Get organization subscription
description: Returns the active subscription for the target org. Poll after async create/patch/cancel operations. Reconcile cancellation on cancelledAt (status may remain ACTIVE until period close). Requires ORG_SUBSCRIPTIONS READ.
operationId: getTenantOrgSubscription
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
responses:
'200':
description: Active subscription.
content:
application/json:
schema:
$ref: '#/components/schemas/OrgSubscriptionDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
post:
tags:
- Tenant Marketplace Provisioning
summary: Create organization subscription
description: Starts the subscription-created workflow (202 Accepted, empty body). Requires X-Idempotency-Key. Returns 403 when subscriptionBillingManagementEnabled is false on the tenant. Returns 409 when the org already has an active non-FREE subscription. Replay with the same idempotency key returns 202 again. Requires ORG_SUBSCRIPTIONS WRITE.
operationId: createTenantOrgSubscription
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- $ref: '#/components/parameters/XIdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTenantOrgSubscriptionRequestDto'
responses:
'202':
description: Subscription creation accepted (async workflow).
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
patch:
tags:
- Tenant Marketplace Provisioning
summary: Update organization subscription
description: Updates plan, seats, or external identifiers. Entitlement changes run via async workflow (202, empty body). Trivial field-only edits may return 200 with OrgSubscriptionDto. Requires X-Idempotency-Key and ORG_SUBSCRIPTIONS WRITE. Returns 403 when subscriptionBillingManagementEnabled is false.
operationId: patchTenantOrgSubscription
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- $ref: '#/components/parameters/XIdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PatchTenantOrgSubscriptionRequestDto'
responses:
'200':
description: Subscription updated synchronously.
content:
application/json:
schema:
$ref: '#/components/schemas/OrgSubscriptionDto'
'202':
description: Subscription change accepted (async workflow).
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
delete:
tags:
- Tenant Marketplace Provisioning
summary: Cancel organization subscription
description: Starts the cancel lifecycle workflow (202 Accepted, empty body). Sets cancelledAt while status may remain ACTIVE until period close. Requires X-Idempotency-Key. Optional request body for provenance metadata. Requires ORG_SUBSCRIPTIONS WRITE. Returns 403 when subscriptionBillingManagementEnabled is false.
operationId: cancelTenantOrgSubscription
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- $ref: '#/components/parameters/XIdempotencyKey'
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/CancelTenantOrgSubscriptionRequestDto'
responses:
'202':
description: Cancellation accepted (async workflow).
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/billing/balances:
get:
tags:
- Tenant Marketplace Provisioning
summary: List organization balances
description: Returns current included and extra balances per balance type. Requires BILLING_BALANCES READ.
operationId: getTenantOrgBalances
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
responses:
'200':
description: Organization balances.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/OrgBalanceDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
post:
tags:
- Tenant Marketplace Provisioning
summary: Top up organization balance
description: 'Credits the org balance (positive amounts only). Requires X-Idempotency-Key mapped to the ledger referenceId; replays return alreadyRecorded: true. Partner convention: balanceType CHAT and targetColumn EXTRA for purchased credits. Requires BILLING_BALANCES WRITE. Returns 403 when subscriptionBillingManagementEnabled is false.'
operationId: topUpTenantOrgBalance
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- $ref: '#/components/parameters/XIdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TenantAdjustBalanceRequestDto'
responses:
'201':
description: Balance credited (or idempotent replay).
content:
application/json:
schema:
$ref: '#/components/schemas/AdjustBalanceResponseDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/billing/balances/ledger:
get:
tags:
- Tenant Marketplace Provisioning
summary: List organization balance ledger
description: Paginated ledger entries for reconciliation. Requires BILLING_BALANCES READ.
operationId: getTenantOrgBalanceLedger
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- name: balanceType
in: query
schema:
$ref: '#/components/schemas/BalanceTypeDto'
- name: since
in: query
schema:
type: string
format: date-time
- name: until
in: query
schema:
type: string
format: date-time
- name: entryType
in: query
schema:
$ref: '#/components/schemas/LedgerEntryTypeDto'
- name: cursor
in: query
schema:
type: string
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 50
responses:
'200':
description: Ledger page.
content:
application/json:
schema:
$ref: '#/components/schemas/BalanceLedgerPageDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/billing/invoices:
get:
tags:
- Tenant Marketplace Provisioning
summary: List organization invoices
description: Paginated invoices for the org subscription. Requires BILLING_INVOICES READ.
operationId: listTenantOrgInvoices
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- name: cursor
in: query
schema:
type: string
minLength: 1
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
'200':
description: Invoice page.
content:
application/json:
schema:
$ref: '#/components/schemas/InvoicePageDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/billing/allocations:
get:
tags:
- Tenant Marketplace Provisioning
summary: List organization allocation limits
description: Returns org-level monthly/daily allocation limits per balance type. Requires BILLING_ALLOCATIONS READ.
operationId: getTenantOrgAllocations
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
responses:
'200':
description: Allocation limits.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/OrgAllocationDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/members:
get:
tags:
- Tenant Marketplace Provisioning
summary: List organization members
description: Paginated member list for seat reconciliation. Requires ORG_SETTINGS READ.
operationId: listTenantOrgMembers
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 10
- name: offset
in: query
schema:
type: integer
minimum: 0
default: 0
responses:
'200':
description: Member page.
content:
application/json:
schema:
$ref: '#/components/schemas/TenantOrganizationMembersPageDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/invitations:
get:
tags:
- Tenant Marketplace Provisioning
summary: List organization invitations
description: Paginated invitation list for reconciliation. Requires ORG_SETTINGS READ.
operationId: listTenantOrgInvitations
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- name: status
in: query
schema:
type: string
enum:
- pending
- accepted
- revoked
default: pending
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 50
- name: offset
in: query
schema:
type: integer
minimum: 0
default: 0
responses:
'200':
description: Invitation page.
content:
application/json:
schema:
$ref: '#/components/schemas/TenantOrganizationInvitationsPageDto'
'400':
description: Bad request.
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
post:
tags:
- Tenant Marketplace Provisioning
summary: Invite organization member
description: Sends an invitation email to add a member by email. Seat caps enforced. Requires ORG_SETTINGS WRITE.
operationId: inviteTenantOrgMember
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTenantOrganizationInvitationRequestDto'
responses:
'201':
description: Invitation sent.
content:
application/json:
schema:
$ref: '#/components/schemas/TenantOrganizationInvitationSuccessDto'
'400':
description: Bad request.
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
'409':
description: Conflict — seat cap exceeded.
'429':
description: Rate limit exceeded.
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
/api/v1/tenant/organizations/{orgId}/members/{userId}:
put:
tags:
- Tenant Marketplace Provisioning
summary: Update organization member
description: Updates role and/or membership type. At least one field required. Requires ORG_SETTINGS WRITE.
operationId: updateTenantOrgMember
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- name: userId
in: path
required: true
description: Target user ID.
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateTenantOrganizationMemberRequestDto'
responses:
'200':
description: Member updated.
content:
application/json:
schema:
$ref: '#/components/schemas/TenantMemberUpdateSuccessDto'
'400':
description: Bad request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
delete:
tags:
- Tenant Marketplace Provisioning
summary: Unassign organization member
description: 'Removes membership and cancels seat assignment. Idempotent: returns 204 even if the member was already removed. Requires ORG_SETTINGS WRITE. Uses a Redis-backed lock; returns 503 when Redis is unavailable. Returns 400 when another deletion is already in progress for the org.'
operationId: unassignTenantOrgMember
parameters:
- name: orgId
in: path
required: true
description: Target organization ID (must belong to the tenant).
schema:
type: string
- name: userId
in: path
required: true
description: Target user ID.
schema:
type: string
responses:
'204':
description: Member unassigned (or already absent).
'400':
description: Bad request — e.g. another member deletion is already in progress for this organization.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized — missing or invalid authentication.
'403':
description: Forbidden — caller is not the tenant owner or cannot access this organization.
'404':
description: Not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Service unavailable — member deletion requires Redis for the concurrency lock.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error.
security:
- ApiKeyAuth: []
components:
schemas:
OrgSubscriptionStatusDto:
type: string
enum:
- ACTIVE
- CANCELLED
- SUSPENDED
- FREE_TRIAL
AllocationLimitDto:
oneOf:
- type: string
enum:
- unlimited
- type: string
pattern: ^\d+(\.\d+)?$
- type: 'null'
description: Dollar cap, explicit unlimited, or null (inherit from next layer).
CreateTenantOrgSubscriptionRequestDto:
type: object
required:
- pricingPlan
- maxUsers
properties:
pricingPlan:
type: string
enum:
- FREE
- FREE_MULTI_USER
- PERSONAL
- BUSINESS
description: Pricing plan allowed on tenant marketplace subscription create/patch.
maxUsers:
type: integer
minimum: 1
description: Paid seat cap.
externalSubscriptionId:
type: string
nullable: true
externalCustomerId:
type: string
nullable: true
allowsFreeUsers:
type: boolean
description: When true, permits RESTRICTED members up to maxFreeUsers.
maxFreeUsers:
type: integer
minimum: 1
nullable: true
metadata:
$ref: '#/components/schemas/TenantSubscriptionMetadataRequestDto'
additionalProperties: false
description: Creates a subscription via async workflow. processorType is set server-side to TENANT. Plan-derived fields (balanceAllocationPerUser, usageLimits, etc.) are not accepted on the wire.
SubscriptionMetadataDto:
type: object
additionalProperties: true
properties:
trialEndDate:
type: string
format: date-time
promo:
type: string
TenantOrganizationInvitationDto:
type: object
required:
- id
- emailAddress
- role
- status
- membershipType
- createdAt
properties:
id:
type: string
emailAddress:
type: string
format: email
role:
type: string
status:
type: string
enum:
- pending
- accepted
- revoked
membershipType:
$ref: '#/components/schemas/OrgMembershipTypeDto'
createdAt:
type: string
format: date-time
nullable: true
BillingPaginationDto:
type: object
required:
- nextCursor
- limit
properties:
nextCursor:
type: string
nullable: true
limit:
type: integer
BalanceLedgerPageDto:
type: object
required:
- data
- pagination
properties:
data:
type: array
items:
$ref: '#/components/schemas/BalanceLedgerEntryDto'
pagination:
$ref: '#/components/schemas/BillingPaginationDto'
SubscriptionProcessorTypeDto:
type: string
enum:
- STRIPE
- APPDIRECT
- APPLE
- TENANT
TenantOrganizationInvitationsPageDto:
type: object
required:
- invitations
- totalCount
properties:
invitations:
type: array
items:
$ref: '#/components/schemas/TenantOrganizationInvitationDto'
totalCount:
type: integer
OrgBalanceDto:
type: object
required:
- balanceType
- includedBalance
- extraBalance
properties:
balanceType:
$ref: '#/components/schemas/BalanceTypeDto'
includedBalance:
type: string
description: Per-cycle included balance (decimal string).
extraBalance:
type: string
description: Top-up / extra balance (decimal string).
PatchTenantOrgSubscriptionRequestDto:
type: object
properties:
pricingPlan:
type: string
enum:
- FREE
- FREE_MULTI_USER
- PERSONAL
- BUSINESS
description: Pricing plan allowed on tenant marketplace subscription create/patch.
maxUsers:
type: integer
minimum: 1
nullable: true
allowsFreeUsers:
type: boolean
maxFreeUsers:
type: integer
minimum: 1
nullable: true
externalSubscriptionId:
type: string
nullable: true
externalCustomerId:
type: string
nullable: true
metadata:
$ref: '#/components/schemas/TenantSubscriptionMetadataRequestDto'
additionalProperties: false
description: Partial update. Status/lifecycle fields are not settable — use DELETE to cancel.
CancelTenantOrgSubscriptionRequestDto:
type: object
properties:
eventMetadata:
type: object
additionalProperties: true
description: Optional provenance metadata for the cancel workflow.
additionalProperties: false
OrgSubscriptionPricingPlanDto:
type: string
enum:
- FREE
- FREE_MULTI
# --- truncated at 32 KB (46 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/appdirect/refs/heads/main/openapi/appdirect-tenant-marketplace-provisioning-api-openapi.yml