Skip to content

Commit b1e8c0f

Browse files
authored
docs: add tenant creation automation guide (#1403)
* docs: add tenant creation automation guide * docs: clarify tenant automation PAT wording * fix: address PR review comments
1 parent 22a8f68 commit b1e8c0f

2 files changed

Lines changed: 253 additions & 0 deletions

File tree

docs/logto-cloud/README.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Developer from '@site/src/assets/developer.svg';
33
import TenantMemberManagement from '@site/src/assets/gear.svg';
44
import PricingAndBilling from '@site/src/assets/key.svg';
55
import McpIcon from '@site/src/assets/mcp.svg';
6+
import TenantAutomation from '@site/src/assets/robot.svg';
67
import CustomDomains from '@site/src/assets/search.svg';
78
import SystemLimit from '@site/src/assets/security.svg';
89

@@ -74,6 +75,16 @@ If you have any questions about cloud services and need additional support, plea
7475
icon: <SystemLimit />,
7576
},
7677
},
78+
{
79+
type: 'link',
80+
label: 'Automate tenant management',
81+
href: '/logto-cloud/automate-tenant-creation',
82+
description:
83+
'Create and manage tenants programmatically with Logto Cloud API and Management API.',
84+
customProps: {
85+
icon: <TenantAutomation />,
86+
},
87+
},
7788
{
7889
type: 'link',
7990
label: 'Logto MCP Server',
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
---
2+
id: automate-tenant-creation
3+
title: Automate tenant management
4+
sidebar_position: 8
5+
---
6+
7+
# Automate tenant management
8+
9+
You can manage Logto Cloud tenants programmatically, including creating tenants and continuing configuration without switching to Console.
10+
11+
This is useful when you need to provision tenants from your own onboarding flow, internal platform, AI agent, or integration automation.
12+
13+
The automation flow is:
14+
15+
1. Use a **Logto Cloud Personal Access Token (PAT)** to call the Logto Cloud API.
16+
2. Create a tenant with `POST /api/tenants`.
17+
3. Read the default Machine-to-machine (M2M) application credentials from the creation response.
18+
4. Use the default M2M application to get a Management API access token for the new tenant.
19+
5. Call the new tenant's Management API to continue provisioning applications, users, roles, resources, organizations, and other settings.
20+
21+
## Before you start \{#before-you-start}
22+
23+
Prepare the following values:
24+
25+
| Variable | Description |
26+
| -------------------- | ---------------------------------------------------------------------------- |
27+
| `CLOUD_API_ENDPOINT` | The Logto Cloud API endpoint. For Logto Cloud, use `https://cloud.logto.io`. |
28+
| `LOGTO_CLOUD_PAT` | A PAT for your Logto Cloud account. |
29+
| `TENANT_NAME` | The display name of the tenant to create. |
30+
| `TENANT_TAG` | The tenant type. Use `development` or `production`. |
31+
| `REGION_NAME` | The region identifier for the tenant. |
32+
33+
Set them as environment variables:
34+
35+
```bash
36+
export CLOUD_API_ENDPOINT="https://cloud.logto.io"
37+
export LOGTO_CLOUD_PAT="<logto-cloud-pat>"
38+
export TENANT_NAME="My automated tenant"
39+
export TENANT_TAG="development"
40+
export REGION_NAME="<region-name>"
41+
```
42+
43+
## Get available regions \{#get-available-regions}
44+
45+
Before creating a tenant, fetch the regions available to your Logto Cloud account:
46+
47+
```bash
48+
curl "$CLOUD_API_ENDPOINT/api/me/regions" \
49+
-H "Authorization: Bearer $LOGTO_CLOUD_PAT"
50+
```
51+
52+
The response contains the available regions. Use the `name` value as `REGION_NAME` when creating the tenant.
53+
54+
Example response:
55+
56+
```json
57+
{
58+
"regions": [
59+
{
60+
"name": "EU",
61+
"displayName": "Europe"
62+
},
63+
{
64+
"name": "US",
65+
"displayName": "United States"
66+
}
67+
]
68+
}
69+
```
70+
71+
## Create a tenant \{#create-a-tenant}
72+
73+
Call `POST /api/tenants` with the Logto Cloud PAT:
74+
75+
```bash
76+
curl "$CLOUD_API_ENDPOINT/api/tenants" \
77+
-X POST \
78+
-H "Authorization: Bearer $LOGTO_CLOUD_PAT" \
79+
-H "Content-Type: application/json" \
80+
-d '{
81+
"name": "'"$TENANT_NAME"'",
82+
"tag": "'"$TENANT_TAG"'",
83+
"regionName": "'"$REGION_NAME"'"
84+
}'
85+
```
86+
87+
The response includes the created tenant and a default M2M application. The M2M application is created in the new tenant and has access to the tenant's Management API.
88+
89+
Example response:
90+
91+
```json
92+
{
93+
"id": "new-tenant-id",
94+
"name": "My automated tenant",
95+
"tag": "development",
96+
"indicator": "https://new-tenant-id.logto.app",
97+
"regionName": "EU",
98+
"defaultApplication": {
99+
"id": "default-m2m-app-id",
100+
"secret": "default-m2m-app-secret"
101+
}
102+
}
103+
```
104+
105+
Save the values you need for the next step:
106+
107+
```bash
108+
export TENANT_ID="<response.id>"
109+
export TENANT_ENDPOINT="<response.indicator>"
110+
export DEFAULT_M2M_APP_ID="<response.defaultApplication.id>"
111+
export DEFAULT_M2M_APP_SECRET="<response.defaultApplication.secret>"
112+
```
113+
114+
## Get a Management API access token for the new tenant \{#get-a-management-api-access-token-for-the-new-tenant}
115+
116+
Use the default M2M application credentials to request an access token from the new tenant:
117+
118+
```bash
119+
curl "$TENANT_ENDPOINT/oidc/token" \
120+
-X POST \
121+
-u "$DEFAULT_M2M_APP_ID:$DEFAULT_M2M_APP_SECRET" \
122+
-H "Content-Type: application/x-www-form-urlencoded" \
123+
-d "grant_type=client_credentials" \
124+
-d "resource=$TENANT_ENDPOINT/api" \
125+
-d "scope=all"
126+
```
127+
128+
Example response:
129+
130+
```json
131+
{
132+
"access_token": "eyJ...",
133+
"expires_in": 3600,
134+
"token_type": "Bearer",
135+
"scope": "all"
136+
}
137+
```
138+
139+
Save the access token:
140+
141+
```bash
142+
export MANAGEMENT_API_ACCESS_TOKEN="<response.access_token>"
143+
```
144+
145+
## Continue provisioning the new tenant \{#continue-provisioning-the-new-tenant}
146+
147+
Use the Management API access token to call the new tenant's Management API.
148+
149+
For example, list applications:
150+
151+
```bash
152+
curl "$TENANT_ENDPOINT/api/applications" \
153+
-H "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN"
154+
```
155+
156+
Or create an application:
157+
158+
```bash
159+
curl "$TENANT_ENDPOINT/api/applications" \
160+
-X POST \
161+
-H "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
162+
-H "Content-Type: application/json" \
163+
-d '{
164+
"name": "My web app",
165+
"type": "SPA",
166+
"oidcClientMetadata": {
167+
"redirectUris": ["https://example.com/callback"],
168+
"postLogoutRedirectUris": ["https://example.com"]
169+
}
170+
}'
171+
```
172+
173+
At this point, your automation can continue with any Management API operation, such as creating users, applications, API resources, roles, organizations, connectors, or sign-in experience settings.
174+
175+
## Full automation example \{#full-automation-example}
176+
177+
The following Node.js example creates a tenant, exchanges the returned default M2M credentials for a Management API access token, and lists applications in the new tenant:
178+
179+
```js
180+
const cloudApiEndpoint = 'https://cloud.logto.io';
181+
const logtoCloudPat = process.env.LOGTO_CLOUD_PAT;
182+
183+
const createTenantResponse = await fetch(`${cloudApiEndpoint}/api/tenants`, {
184+
method: 'POST',
185+
headers: {
186+
authorization: `Bearer ${logtoCloudPat}`,
187+
'content-type': 'application/json',
188+
},
189+
body: JSON.stringify({
190+
name: 'My automated tenant',
191+
tag: 'development',
192+
regionName: 'EU',
193+
}),
194+
});
195+
196+
if (!createTenantResponse.ok) {
197+
throw new Error(`Failed to create tenant: ${await createTenantResponse.text()}`);
198+
}
199+
200+
const tenant = await createTenantResponse.json();
201+
const tenantEndpoint = tenant.indicator;
202+
const { id: appId, secret: appSecret } = tenant.defaultApplication;
203+
204+
const tokenResponse = await fetch(`${tenantEndpoint}/oidc/token`, {
205+
method: 'POST',
206+
headers: {
207+
authorization: `Basic ${Buffer.from(`${appId}:${appSecret}`).toString('base64')}`,
208+
'content-type': 'application/x-www-form-urlencoded',
209+
},
210+
body: new URLSearchParams({
211+
grant_type: 'client_credentials',
212+
resource: `${tenantEndpoint}/api`,
213+
scope: 'all',
214+
}),
215+
});
216+
217+
if (!tokenResponse.ok) {
218+
throw new Error(`Failed to get Management API token: ${await tokenResponse.text()}`);
219+
}
220+
221+
const { access_token: managementApiAccessToken } = await tokenResponse.json();
222+
223+
const applicationsResponse = await fetch(`${tenantEndpoint}/api/applications`, {
224+
headers: {
225+
authorization: `Bearer ${managementApiAccessToken}`,
226+
},
227+
});
228+
229+
if (!applicationsResponse.ok) {
230+
throw new Error(`Failed to list applications: ${await applicationsResponse.text()}`);
231+
}
232+
233+
const applications = await applicationsResponse.json();
234+
235+
console.log({ tenantId: tenant.id, applications });
236+
```
237+
238+
## Related resources \{#related-resources}
239+
240+
- [Personal access token](/user-management/personal-access-token)
241+
- [Interact with Management API](/integrate-logto/interact-with-management-api)
242+
- [Machine-to-machine quick start](/quick-starts/m2m)

0 commit comments

Comments
 (0)