Client Orgs
Provision, list, and deprovision the isolated Wexio organizations behind your customers
Each of your customers gets its own Wexio organization, isolated from every other. These three operations are the whole lifecycle.
Scope: PARTNER_ADMIN. All three accept either an API key holding that scope or a member login on the provider org - which must be of kind TECH_PROVIDER either way. That kind is the sole identifier; there is no capability flag to check.
They act on your provider org, not on one client, so X-Wexio-Org plays no part here.
createClientOrg
createClientOrg(input: CreateClientOrgInput!): Organisation!API key or dashboard login.
Creates an isolated client org owned by your provider org, with partnerRelation: MANAGED.
partnerRelation: MANAGED is what makes an org your child - its usage rolls up to you and it has no retail plan of its own. An org merely referred by a distribution partner also carries a partnerOf pointer but is REFERRED, not MANAGED: it stays an ordinary retail customer and is not a child. Only MANAGED counts.
Arguments - input: CreateClientOrgInput!
| Field | Type | Required | Description |
|---|---|---|---|
name | String! | Yes | Display name of the client org. |
slug | String | No | URL slug. Generated from name when omitted. |
externalId | String | No | Your identifier for this client. Echoed on every webhook as meta.externalId, so you can route events without a lookup. Set it. |
tenantDbUri | String | No | A MongoDB connection string for the client's own database. Omit it and the child inherits your provider org's tenantDbUri automatically. |
tenantRedisUrl | String | No | A Redis URL for the client. Same rule - omit it and the child inherits your provider org's. |
Bring Your Own Database
Both URIs are validated before the org is created, and the server opens a test connection first. A client org is never created pointing at infrastructure that does not answer.
| Rule | Detail |
|---|---|
| Schemes | mongodb:// or mongodb+srv:// for the database; redis:// or rediss:// for Redis. Anything else is rejected. |
| Hosts | Every host is resolved, including the targets behind an mongodb+srv record and the replica-set members the server advertises. Private, loopback, link-local and metadata addresses are rejected. |
| Mongo query options | Allowlisted. Common ones pass: authSource, replicaSet, retryWrites, w, readPreference, tls, timeouts, pool sizes, appName, compressors. Rejected: proxy*, tls*File, tlsInsecure, tlsAllowInvalid*, authMechanismProperties, srvServiceName, and any authMechanism other than SCRAM-SHA-1 or SCRAM-SHA-256. |
| Limits | At most 10 seed hosts or SRV records, at most 50 advertised members, and a 15-second probe deadline. |
| Blank or omitted | Inherits your provider org's URIs. |
A rejected URI returns a generic error with no addresses in it. That is deliberate - a detailed failure would turn this endpoint into a network probe - so debug the connection string on your own infrastructure rather than from the error text.
The rules rule out the shapes that would let a connection string reach inside our network or weaken TLS. If a legitimate option of yours is refused, that is worth raising rather than working around.
Checking a Client's Own Infrastructure
An org with its own database or Redis exposes tenantConnection:
tenantConnection: {
database: TenantDatabaseStatus! // SHARED | CONNECTED | DISCONNECTED
redis: TenantRedisStatus! // SHARED | CONNECTED | DISCONNECTED
checkedAt: Date
reason: String
}null means the org runs on shared infrastructure. The field is readable only by the org itself - its own members or its own API key. Anyone else, including a provider reading its child, gets null.
Returns Organisation! - the created org. Useful fields:
| Field | Type | Description |
|---|---|---|
_id | String! | The org ID. This is the value you send as X-Wexio-Org. |
name | String! | Display name. |
slug | String! | URL slug. |
externalId | String | Your identifier, as supplied. |
partnerOf | String | Your provider org's ID. |
partnerRelation | PartnerRelation | MANAGED for orgs created this way. |
Tenant storage is inherited, not blank. A MANAGED child auto-inherits the provider org's tenantDbUri and tenantRedisUrl, so a provider on its own cluster gets every client on that cluster without passing anything. Set them explicitly only when a specific client needs its own storage - for data-residency reasons, say.
Once created, an API key on your provider org acts on this client by naming its _id in X-Wexio-Org - see Authentication.
Organisation has no createdAt field, so a client-org row carries no creation timestamp. Record the time on your side if you need it.
A key sees a deliberately narrow organisation. Commercial and account fields - billing address, plan, usage credits, referral and lead source, onboarding data, the partner contract and its billing, invite code, coupon policy, and the member list - always read as null for an API key. They are visible only to a member signed into the dashboard.
Ask only for the fields above. A query selecting the others succeeds and returns nothing useful.
Example
mutation CreateClientOrg {
createClientOrg(input: {
name: "Pizzeria Roma"
externalId: "crmco-client-5541"
tenantDbUri: "mongodb+srv://…/roma"
}) {
_id
name
slug
externalId
partnerRelation
}
}{
"data": {
"createClientOrg": {
"_id": "6716b2f0a1c34d0012ab89ef",
"name": "Pizzeria Roma",
"slug": "pizzeria-roma",
"externalId": "crmco-client-5541",
"partnerRelation": "MANAGED"
}
}
}clientOrgs
clientOrgs: [Organisation!]!API key or dashboard login.
Lists every client org your provider org manages. Takes no arguments and is not paginated.
Returns [Organisation!]! - same type as above.
Example
query ListClientOrgs {
clientOrgs {
_id
name
externalId
partnerRelation
}
}{
"data": {
"clientOrgs": [
{
"_id": "6716b2f0a1c34d0012ab89ef",
"name": "Pizzeria Roma",
"externalId": "crmco-client-5541",
"partnerRelation": "MANAGED"
}
]
}
}deprovisionClientOrg
deprovisionClientOrg(orgId: String!): OrganisationCleanupResult!API key or dashboard login.
Tears a client down: disconnects its channels, deletes its Wexio-side data, and emits organisation.deleted to your webhook.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
orgId | String! | Yes | The client org to deprovision. Must be one of your managed clients. |
Returns OrganisationCleanupResult!
| Field | Type | Description |
|---|---|---|
success | Boolean! | true when the cascade completed without errors. |
organisationId | String! | The org that was torn down. |
deletedCounts | CleanupDeletedCounts! | Per-collection delete counts. ~50 integer fields - request only the ones you care about. |
errors | [String!]! | Non-fatal problems hit during the cascade. Empty on a clean run. |
duration | Int! | How long the cascade took, in milliseconds. |
Commonly requested deletedCounts fields: messages, chats, conversations, people, integrations, media, flows, webhookConnections.
deprovisionClientOrg is destructive and not reversible. It disconnects the client's channels and deletes their data on Wexio's side.
Example
mutation Deprovision {
deprovisionClientOrg(orgId: "6716b2f0a1c34d0012ab89ef") {
success
organisationId
duration
errors
deletedCounts { messages chats conversations people integrations }
}
}{
"data": {
"deprovisionClientOrg": {
"success": true,
"organisationId": "6716b2f0a1c34d0012ab89ef",
"duration": 4182,
"errors": [],
"deletedCounts": {
"messages": 12480,
"chats": 311,
"conversations": 311,
"people": 297,
"integrations": 2
}
}
}
}organisation.deleted is emitted before the cascade runs, so the child org still resolves for fan-out. Expect the webhook to arrive ahead of the mutation's response.