Guide

B2B or B2C: how your organisation is set up

Your organisation is either B2B, where every user belongs to one of the businesses you serve, or B2C, where you serve the public. It decides what every call must send.

What you'll learn
  • What B2B and B2C mean, and who decides
  • What to send on each call, with curl, TypeScript and Python
  • How to register customers and link channels and phone numbers
  • The error codes, and the roles and key scope for data requests

What each means

B2BB2C
Who your users arePeople who work for the businesses you sell to (your customers). Every user belongs to one customer.Members of the public. There are no customers.
What a call namesThe user (endUserId / X-End-User-Id) and their customer (customerGroupId / X-Customer-Group-Id).The user only.
What you get per customerLimits, tiers, documents, usage, billing lines, support access and data requests per customer.Limits, usage and data requests per user.

Who sets it

The Dezifi team sets it when we create your organisation. It applies to every workspace in the organisation and cannot be changed later: an attempt is refused with BUSINESS_TYPE_LOCKED. If you are unsure which you are, ask your Dezifi contact before onboarding.

What to send on each call

Every call your server makes for a user names that user. In a B2B organisation it also names their customer, by your own id for it (the customer's external id). In a B2C organisation it never names a customer.
B2B (curl)
# B2B: name the customer the user belongs to
curl -X POST https://api.dezifi.ai/api/v1/runtime/agents/<agentId>/session-tokens \
  -H "Authorization: Bearer $DEZIFI_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "endUserId": "user-123", "customerGroupId": "northwind" }'

# Server-side calls for that user carry both headers
curl -X POST https://api.dezifi.ai/api/v1/runtime/agents/<agentId>/sessions \
  -H "Authorization: Bearer $DEZIFI_SECRET_KEY" \
  -H "X-End-User-Id: user-123" \
  -H "X-Customer-Group-Id: northwind" \
  -H "Content-Type: application/json" -d '{}'
B2C (curl)
# B2C: the user only; no customer
curl -X POST https://api.dezifi.ai/api/v1/runtime/agents/<agentId>/session-tokens \
  -H "Authorization: Bearer $DEZIFI_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "endUserId": "user-123" }'

curl -X POST https://api.dezifi.ai/api/v1/runtime/agents/<agentId>/sessions \
  -H "Authorization: Bearer $DEZIFI_SECRET_KEY" \
  -H "X-End-User-Id: user-123" \
  -H "Content-Type: application/json" -d '{}'
TypeScript
import { DezifiClient } from '@dezifi/client';

const dezifi = new DezifiClient({
  baseUrl: 'https://api.dezifi.ai',
  apiKey: process.env.DEZIFI_SECRET_KEY,
  workspaceId: Number(process.env.DEZIFI_WORKSPACE_ID),
});
const agent = dezifi.agent(process.env.DEZIFI_AGENT_ID ?? '');

// B2B: the customer is required on every end-user call
const b2bToken = await agent.mintSessionToken('user-123', 900, { customerGroupId: 'northwind' });
const asB2bUser = dezifi.onBehalfOf({ endUserId: 'user-123', customerGroupId: 'northwind' });

// B2C: the user only
const b2cToken = await agent.mintSessionToken('user-123', 900);
const asB2cUser = dezifi.onBehalfOf({ endUserId: 'user-123' });
Python
from datetime import timedelta

# B2B: the customer is required on every end-user call
b2b_token = agent.mint_session_token("user-123", ttl=timedelta(minutes=15), customer_group_id="northwind")
as_b2b_user = client.on_behalf_of("user-123", "northwind")

# B2C: the user only
b2c_token = agent.mint_session_token("user-123", ttl=timedelta(minutes=15))
as_b2c_user = client.on_behalf_of_end_user("user-123")
Java: agent.mintSessionToken(SessionTokenRequest.forEndUser("user-123").inCustomerGroup("northwind")) and client.onBehalfOf("user-123", "northwind") for B2B; agent.mintSessionToken("user-123") and client.onBehalfOfEndUser("user-123") for B2C.

B2B: register your customers first

A call can only name a customer the workspace already has. Add them before your users arrive:
  • First load: a CSV. In Admin > Customers, click Import CSV and choose a file with the columns externalId, displayName and, optionally, region. You see the rows before anything is added; rows with a missing or malformed id are listed, not imported.
  • New customers afterwards: the API. When a business signs up to your product, add it from your backend with a secret key that has customer-group:write (the SDKs: customerGroups().create() / customer_groups().create()). One at a time also works in Admin > Customers with Add customer.
  • The external id is your own id for the customer: letters, digits, dots, underscores, colons and dashes, up to 128 characters.
customers.csv
externalId,displayName,region
northwind,Northwind Traders,eu
globex,Globex Corporation
add one customer (curl)
curl -X POST https://api.dezifi.ai/api/v1/customer-groups \
  -H "Authorization: Bearer <secret key with customer-group:write>" \
  -H "Content-Type: application/json" \
  -d '{ "externalId": "northwind", "displayName": "Northwind Traders" }'

B2B: channels and phone numbers belong to a customer

In a B2B organisation every messaging channel (Slack, WhatsApp, email and the rest) and every phone number serves one customer: its senders and callers are that customer's users, and their conversations, runs and usage are that customer's. The Connect dialog asks for the customer, and it cannot be left empty. In a B2C organisation there is no customer field. In DAK, set customerGroupId on a PhoneChannel (and on a schedule Trigger that runs for one customer) only in a B2B organisation.

Anonymous widget visitors

A website widget with a public key and Signed-in users only turned off serves anonymous visitors in both B2B and B2C. They name no customer and are not refused. For signed-in users use session tokens, which name the user (and, in B2B, their customer) as above.

Error codes and what to do

Errors come back as { "error": { "code": "…", "message": "…" } }.
CodeHTTPWhenWhat to do
CUSTOMER_REQUIRED400B2B: an end-user call (a token mint, a call on behalf of a user, a channel or phone number, a webhook end user) named no customer.Send customerGroupId (token mint) or X-Customer-Group-Id with X-End-User-Id; link the channel or number to a customer.
CUSTOMERS_NOT_USED403B2C: the call named a customer, or used a customer-only feature (customers, tiers, bulk changes, per-customer billing and usage, support access, customer datasets).Leave the customer out. Those features are for B2B organisations only.
INVALID_ARGUMENT400B2B: the customer you named is not registered in the workspace ("Customer group '…' does not exist in this workspace").Add it under Admin > Customers or with the API first, then retry.
BUSINESS_TYPE_LOCKED409Someone tried to change the organisation's business type after onboarding.It is set by Dezifi at onboarding and cannot be changed in the product. Contact your Dezifi contact.
ACCESS_DENIED403The person or key lacks the permission, for example a key without privacy:manage forgetting an end user.Give the role or the key the permission (see Roles below), or use a key that has it.

With SDK 0.1.0-beta.3, read code from the error body in the exception message. The next SDK release adds a typed error for each, so you can catch it instead; each one is still the error type its HTTP status always produced (AuthError / DezifiAuthError / DezifiAuthException for a 403), so existing handling keeps working.

CodeTypeScriptPythonJava
CUSTOMER_REQUIREDCustomerRequiredErrorCustomerRequiredErrorCustomerRequiredException
CUSTOMERS_NOT_USEDCustomersNotUsedErrorCustomersNotUsedErrorCustomersNotUsedException
BUSINESS_TYPE_LOCKEDBusinessTypeLockedErrorBusinessTypeLockedErrorBusinessTypeLockedException
ACCESS_DENIEDPermissionDeniedErrorPermissionDeniedErrorPermissionDeniedException
TypeScript (next SDK release)
import { CustomerRequiredError, CustomersNotUsedError, PermissionDeniedError } from '@dezifi/client';

try {
  await agent.mintSessionToken(user.id, 900, { customerGroupId: user.orgId });
} catch (err) {
  if (err instanceof CustomerRequiredError) { /* B2B: send customerGroupId */ }
  else if (err instanceof CustomersNotUsedError) { /* B2C: leave the customer out */ }
  else if (err instanceof PermissionDeniedError) { /* the key lacks a permission */ }
  else throw err;
}

Roles for the people around your customers

There are three built-in roles. Give them in Admin > Users; see what each holds in Admin > Roles.
RoleCanCannot
AdminEverything: build and run agents, set up customers, read usage and cost, export the per-customer invoice lines, answer data requests, manage users and roles.—
MemberBuild and run agents, read Monitor, read customers and their usage. A workspace admin also sets up the customers of their workspace.Export billing, answer data requests or manage users.
ViewerRead agents, runs and customers.See usage or cost, export billing, answer data requests or change anything.

Custom roles can combine the four customer permissions: customers:manage, usage:read, billing:export and privacy:manage. For someone in finance, make a role with customer-group:read, usage:read and billing:export; for someone who answers data requests, one with privacy:manage (and customer-group:read to pick the customer in Admin > Privacy). Admins have all four.

Support access limits a person, whatever their role, to the customers they support: set it under Admin > Customers > Support access. They then see only those customers' conversations, usage and Analytics, and every conversation they open is on that customer's audit trail.

Data requests from your backend need privacy:manage

To forget or export an end user from your own systems, use a secret key whose permissions include privacy:manage and that belongs to a workspace Admin or Owner. A key with only agent:execute is refused with ACCESS_DENIED. Widget keys and session tokens can never do it.
forget one end user (curl)
# B2B: name the person's customer. B2C: leave ?customerGroupId=... out.
curl -X POST "https://api.dezifi.ai/api/v1/end-users/user-123/forget?customerGroupId=northwind" \
  -H "Authorization: Bearer <secret key with privacy:manage>"
SDKs: customerGroups().forgetEndUser(id, customer?) and exportEndUser (TypeScript, Java), customer_groups().forget_end_user(id, customer_group_id=None) and export_end_user (Python). Name the customer in B2B; leave it out in B2C.

Frequently asked questions

Can I switch from B2C to B2B later?
Not in the product: the business type is set by Dezifi at onboarding and changes are refused (BUSINESS_TYPE_LOCKED). Talk to your Dezifi contact before onboarding if you are unsure.
In B2B, what about a user who is not part of any customer?
Every signed-in user of a B2B organisation belongs to a customer. Anonymous widget visitors are the exception: they name no customer.