InfiniSynapse Partner Silent Provisioning Guide
This guide is for third-party developers who want to silently embed InfiniSynapse capabilities into their own products. Unlike "Sign in with InfiniSynapse" (see the InfiniSynapse Partner SSO Integration Guide), this approach never requires your users to log in or be redirected: your backend calls a set of M2M (server-to-server) APIs to automatically provision a dedicated InfiniSynapse sub-account and API key for each of your users, then calls the InfiniSynapse open APIs on their behalf.
After completing this integration you can:
- Silently provision an independent InfiniSynapse sub-account for each of your users (completely invisible to them — no InfiniSynapse sign-up or login required);
- Obtain a dedicated API key (starting with
sk-) per sub-account and call InfiniSynapse open APIs (e.g. launching data-analysis tasks) from your backend on the user's behalf; - Keep every sub-account's data and tasks isolated, while all usage is billed to your main account (the InfiniSynapse account that created the integration app) — you simply keep the main account topped up; there is no need to fund each sub-account separately;
- Rotate keys, disable sub-accounts, and query usage and the main-account balance for reconciliation at any time.
All you need is a backend that can send HTTP requests. There is no SDK dependency — any language works.
1. How it works
Your backend InfiniSynapse
│ │
│ ① Provision (once per user) │
│ ──── POST /users/provision ────▶│ Idempotent on (clientId, externalUserId):
│ ◀─── { infiniUserId, apiKey } ──│ creates account + issues dedicated API key
│ │
│ ② Call open APIs (day-to-day usage)
│ ── Authorization: Bearer sk-xxx ▶│ usage billed to your main account's balance
│ ◀────────── result ─────────────│
- Provision: the first time you need InfiniSynapse for one of your users, call the provisioning endpoint once with that user's unique ID in your system (
externalUserId). InfiniSynapse creates an independent sub-account and issues a dedicated API key. Repeated calls with the sameexternalUserIdare idempotent — you always get the same sub-account and the same valid key, so retries are safe. - Call: with the
sk-key in hand, call InfiniSynapse open APIs from your backend. All sub-account usage is billed to your main account's balance — just keep your main account topped up in InfiniSynapse, and its balance serves as the shared funding source for all sub-accounts.
Sub-accounts cannot log in to the InfiniSynapse App by default (no password is set). They are purely backend-managed accounts with no separate wallet and cannot be topped up individually. API key calls are unaffected.
Billing model: sub-accounts share the main account's balance
- Main account = the account you (the developer) used to log in to InfiniSynapse and create the integration app (
clientId). - Usage from all your sub-accounts is deducted from the main account's balance (in the same order the main account itself uses: gift balance → subscription quota → paid balance).
- You do not top up sub-accounts individually, and there is no separate prepaid pool; just keep the main account's balance sufficient.
- Usage records are still kept per sub-account, so you can tell which user generated which consumption (see 4.5 for reconciliation).
2. Prerequisites: integration credentials
This flow shares the same credentials as Partner SSO: clientId + clientSecret. If you have already integrated "Sign in with InfiniSynapse", reuse your existing credentials — no new application needed.
If you don't have credentials yet:
- Log in to https://app.infinisynapse.com/tasks with your InfiniSynapse account (this account is the paying main account for all sub-account usage, so keep its balance sufficient);
- Click the "Settings" gear icon at the bottom left and choose Third-party integrations;
- Click Create integration app and fill in the app name (the callback-domain whitelist is only used by the login flow; any value is fine for this guide);
- On success, a dialog shows your
clientId/clientSecret. The secret is shown only once — store it safely right away.
clientSecretis effectively your app's password. Keep it on your backend only (environment variables, secret managers, etc.). Never put it in frontend code, mobile clients, or public repositories. If you suspect a leak, reset it on the page immediately.
3. API basics
| Item | Value |
|---|---|
| API base URL (China) | https://api.infinisynapse.cn/api |
| API base URL (Global) | https://api.infinisynapse.com/api |
| Authentication | request headers X-Client-Id + X-Client-Secret |
| Content type | application/json |
All endpoints return a unified envelope:
{
"code": 200,
"message": "success",
"data": { }
}
code === 200 means success with the payload in data; on failure message describes the error. Examples below use the .com domain — replace with .cn for the China region.
4. Core endpoints
4.1 Provision / fetch a sub-account and API key (idempotent)
Call this once the first time you need a given user, then cache the returned apiKey in your user table — no need to call it on every request.
POST /api/auth/partner/users/provision
X-Client-Id: partner_xxxxxxxx
X-Client-Secret: psk_xxxxxxxx
Content-Type: application/json
Request body:
| Field | Required | Description |
|---|---|---|
externalUserId | Yes | The user's unique ID in your system (≤128 chars). It is the sub-account's mapping key — never change it once used |
withApiKey | No | Defaults to true, issuing/reusing a dedicated API key; pass false to create the account only |
profile | No | Optional profile info for the account: email, nickname, avatar, phone |
Response example:
{
"code": 200,
"message": "success",
"data": {
"infiniUserId": "68d21916d6802ec254b46975",
"externalUserId": "your-user-123",
"apiKey": "sk-xxxxxxxxxxxxxxxxxxxxxxxx",
"created": true
}
}
infiniUserId: the sub-account's unique ID in InfiniSynapse; store it alongsideexternalUserIdin your user table;created:truemeans a new account was created this time;falsemeans it already existed (idempotent reuse);- Repeated calls with the same
(clientId, externalUserId)always return the same sub-account and the same valid key. If the key was deleted or invalidated, a new one is issued automatically — so "just call provision again" is always a safe fallback.
4.2 Rotate an API key (leak response)
POST /api/auth/partner/users/apikey/rotate
Body: { "externalUserId": "your-user-123" }
Response data: { "infiniUserId": "...", "apiKey": "sk-newkey" }. The old key becomes invalid immediately — overwrite your cached value with the new one.
4.3 Disable a sub-account
POST /api/auth/partner/users/revoke
Body: { "externalUserId": "your-user-123" }
Response data: { "infiniUserId": "...", "status": "disabled" }. After disabling:
- The sub-account's API key is deleted and can no longer call any API;
- The sub-account is flagged as disabled; subsequent
provision/rotatecalls for it are rejected; - Disabling only affects that sub-account's ability to call APIs; it does not affect the main account's balance or other sub-accounts.
4.4 Query sub-account status
GET /api/auth/partner/users/{externalUserId}
Response data:
{
"infiniUserId": "...",
"externalUserId": "your-user-123",
"status": "active",
"hasApiKey": true,
"created": 1751443200000,
"lastProvisionAt": 1751443200000,
"disabledAt": null
}
4.5 Query balance (reconciliation)
Since sub-accounts share the main account's funds, this endpoint returns the main account's currently available balance (the shared pool used by all your sub-accounts).
GET /api/auth/partner/users/{externalUserId}/balance
Response data:
{
"infiniUserId": "...",
"billingUserId": "main-account userId",
"shared": true,
"orderBalance": 128.5,
"totalBalance": 128.5
}
billingUserId: the actual billing account, i.e. your main account;orderBalance/totalBalance: the main account's available balance (paid balance + unexpired gift balance; excludes the daily subscription quota);- No matter which
externalUserIdyou query with,orderBalancepoints to the same main-account balance.
4.6 List all your sub-accounts (paginated)
GET /api/auth/partner/users?page=1&pageSize=50&status=active
| Parameter | Description |
|---|---|
page | Page number, default 1 |
pageSize | Items per page, default 50, max 100 |
status | Optional, active / disabled |
Response data:
{
"list": [
{
"externalUserId": "your-user-123",
"infiniUserId": "...",
"status": "active",
"hasApiKey": true,
"createTime": 1751443200000,
"lastProvisionAt": 1751443200000,
"disabledAt": null
}
],
"total": 1,
"page": 1,
"pageSize": 50
}
4.7 Query credit usage (per sub-account / per task, for reconciliation)
Even though deductions all land on the main account, usage records are still kept per sub-account, so you can see exactly "which user, on which task, consumed how many credits" — useful for internal cost allocation, metering, or risk control. Three query endpoints are provided.
The startTime / endTime parameters are millisecond timestamps; pass either, both, or neither (omit to query everything).
4.7.1 Sub-account usage details / grouped by task
GET /api/auth/partner/users/{externalUserId}/costs?page=1&pageSize=20&startTime=&endTime=&groupByTask=false
| Parameter | Description |
|---|---|
page | Page number, default 1 |
pageSize | Items per page, default 20, max 200 |
startTime / endTime | Optional, millisecond timestamp range |
groupByTask | Optional; true aggregates by task (a parent task automatically merges its sub-agent usage); defaults to per-record details |
By default (groupByTask=false), returns per-record details:
{
"infiniUserId": "...",
"externalUserId": "your-user-123",
"groupByTask": false,
"list": [
{ "taskId": "c3a2f9d0-...", "chatId": "...", "cost": 1.2, "model": "gpt-x", "createTime": 1751443200000 }
],
"total": 37,
"page": 1,
"pageSize": 20
}
With groupByTask=true, each row is one task's total usage:
{
"groupByTask": true,
"list": [
{ "taskId": "c3a2f9d0-...", "totalCost": 5.8, "count": 6, "models": ["gpt-x"], "firstTime": 1751443200000, "lastTime": 1751443800000 }
],
"total": 12,
"page": 1,
"pageSize": 20
}
4.7.2 Sub-account usage summary (total + per-model)
GET /api/auth/partner/users/{externalUserId}/cost-stats?startTime=&endTime=
Response data:
{
"infiniUserId": "...",
"externalUserId": "your-user-123",
"totalCost": 42.6,
"recordCount": 37,
"modelStats": [
{ "model": "gpt-x", "cost": 30.1, "count": 20 },
{ "model": "gpt-y", "cost": 12.5, "count": 17 }
]
}
4.7.3 Client-level usage summary (all your sub-accounts)
Get a consumption ranking across all your sub-accounts in one call, for overall reconciliation:
GET /api/auth/partner/costs?page=1&pageSize=50&startTime=&endTime=
| Parameter | Description |
|---|---|
page | Page number, default 1 |
pageSize | Items per page, default 50, max 200 |
startTime / endTime | Optional, millisecond timestamp range |
Response data (sorted by totalCost descending):
{
"clientId": "partner_xxxxxxxx",
"grandTotalCost": 128.9,
"subAccountCount": 25,
"list": [
{ "externalUserId": "your-user-123", "infiniUserId": "...", "totalCost": 42.6, "recordCount": 37 },
{ "externalUserId": "your-user-456", "infiniUserId": "...", "totalCost": 30.2, "recordCount": 21 }
],
"total": 18,
"page": 1,
"pageSize": 50
}
grandTotalCost: total usage of all your sub-accounts within the time range (matches what was deducted from the main account);subAccountCount: total number of your sub-accounts;total: number of sub-accounts with usage (those with none are not included in the paginated list).
5. Calling InfiniSynapse open APIs with the key
With a sub-account's sk- key you can call open APIs on the user's behalf from your backend. Task-related open APIs live on the App service domain (note: different from the api. domain used by the M2M endpoints above): https://app.infinisynapse.cn in China, https://app.infinisynapse.com globally.
Example: launch a data-analysis task on behalf of a user
POST https://app.infinisynapse.com/api/ai/message
Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"type": "newTask",
"taskId": "c3a2f9d0-a-uuid-you-generate-for-idempotency",
"text": "Analyze the last 7 days of sales data and produce a chart report",
"images": []
}
Generate taskId on your backend (UUID recommended); resubmitting the same taskId will not create duplicate tasks. The task belongs to this sub-account (data/task isolation), while its usage is deducted from your main account's balance.
Tip: set up a low-watermark alert on your main account's balance (poll the 4.5 endpoint periodically). An empty balance will limit calls for all sub-accounts.
6. Security notes
- Keep
clientSecretand allsk-keys on the backend only. They are long-lived credentials and must never reach browsers, mobile clients, or public repositories. - One key maps to exactly one sub-account, so a leak's blast radius is limited to that sub-account's own data. On any anomaly, call
rotateimmediately, orrevoketo disable the account. - The main account's balance is a shared pool used by all your sub-accounts. Implement your own usage monitoring and quota controls so that one abnormal user cannot drain the whole balance.
- Never change an
externalUserIdonce used — it is the sub-account's unique mapping key; a new ID means a new account. - Silent provisioning means you create accounts on your users' behalf: you are responsible for your users' data authorization and compliance. Collect the minimum necessary
profilefields. - The provisioning endpoint is rate-limited and each app has a sub-account cap; normal business volume won't hit them, but contact us in advance for bulk imports.
- If a secret may have leaked, reset
clientSecretright away on the Settings → Third-party integrations page (the old secret becomes invalid immediately).
7. Endpoint quick reference
| Endpoint | Method | Purpose |
|---|---|---|
/api/auth/partner/users/provision | POST | Provision / fetch a sub-account and API key (idempotent) |
/api/auth/partner/users/apikey/rotate | POST | Rotate a sub-account's API key (old key invalidated immediately) |
/api/auth/partner/users/revoke | POST | Disable a sub-account (delete key + flag disabled) |
/api/auth/partner/users/{externalUserId} | GET | Query sub-account status |
/api/auth/partner/users/{externalUserId}/balance | GET | Query main-account available balance (reconciliation) |
/api/auth/partner/users | GET | List your sub-accounts (paginated) |
/api/auth/partner/users/{externalUserId}/costs | GET | Query sub-account usage details (optionally grouped by task) |
/api/auth/partner/users/{externalUserId}/cost-stats | GET | Query sub-account usage summary (total + per-model) |
/api/auth/partner/costs | GET | Client-level usage summary (ranking across all sub-accounts) |
All endpoints authenticate via the X-Client-Id + X-Client-Secret request headers.
8. Complete example (Node.js)
A minimal runnable example covering the full chain "provision → launch a task on the user's behalf" (funded automatically by the main account's balance):
const PROXY_API = 'https://api.infinisynapse.com/api'
const APP_API = 'https://app.infinisynapse.com/api'
const partnerHeaders = {
'Content-Type': 'application/json',
'X-Client-Id': process.env.INFINI_CLIENT_ID,
'X-Client-Secret': process.env.INFINI_CLIENT_SECRET,
}
async function callPartner(path, options = {}) {
const resp = await fetch(`${PROXY_API}${path}`, { headers: partnerHeaders, ...options })
const json = await resp.json()
if (json.code !== 200) throw new Error(`${path} failed: ${json.message}`)
return json.data
}
// 1. First time you need a user: provision the sub-account and cache the apiKey
// (idempotent, safe to retry)
async function ensureInfiniAccount(user) {
const data = await callPartner('/auth/partner/users/provision', {
method: 'POST',
body: JSON.stringify({
externalUserId: user.id,
profile: { nickname: user.nickname, email: user.email },
}),
})
// Persist data.infiniUserId / data.apiKey in your user table
return data
}
// 2. Launch an analysis task on the user's behalf (billed to the main account)
async function createTask(apiKey, text) {
const resp = await fetch(`${APP_API}/ai/message`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({ type: 'newTask', taskId: crypto.randomUUID(), text, images: [] }),
})
return resp.json()
}
// Putting it together
const { apiKey } = await ensureInfiniAccount({ id: 'your-user-123', nickname: 'Alice' })
await createTask(apiKey, 'Analyze the last 7 days of sales data and produce a chart report')
9. Relationship with "Sign in with InfiniSynapse"
The two integration paths coexist and complement each other, sharing the same clientId / clientSecret:
| Partner SSO login | Silent provisioning (this guide) | |
|---|---|---|
| Does the user log in to InfiniSynapse? | Yes (one authorization-code login) | No — completely invisible |
| Account ownership | The user's own InfiniSynapse account | An independent sub-account managed by you |
| Billing | The user's own balance / subscription | Billed entirely to your main account's balance |
| Best for | Users who have or are willing to create an InfiniSynapse account | Bundling InfiniSynapse capabilities into your product without users ever noticing |
10. FAQ
Q: Whose account is sub-account usage billed to? Your main account (the InfiniSynapse account you used to create the integration app). All your sub-accounts share this single funding pool; you just keep the main account topped up.
Q: Do I need to top up each sub-account individually? No. Sub-accounts have no separate wallet and cannot be topped up individually; all funds come from the main account's balance.
Q: Will repeated provision calls create duplicate accounts or multiple keys?
No. The same externalUserId always maps to the same sub-account and the same valid key. The endpoint is idempotent and safe to retry; if you lose a cached key, just call provision again to recover it.
Q: Can a sub-account log in to the InfiniSynapse App? Not by default (no password is set). It is a purely backend-managed account; API key calls are unaffected.
Q: What happens when the main account's balance runs out? Calls from all your sub-accounts become limited by balance/quota. Combine the balance endpoint in 4.5 with low-watermark monitoring to top up the main account in time.
Q: How do I know how much each user consumed?
Usage records are kept per sub-account (infiniUserId), while the deduction always lands on the main account. Use the usage-query endpoints in 4.7 for precise reconciliation: per-sub-account details / grouped by task (4.7.1), a per-sub-account summary (4.7.2), or a one-shot consumption ranking across all your sub-accounts (4.7.3).
Q: What should I use as externalUserId?
A stable, immutable primary key from your system (e.g. a database ID). Avoid emails or phone numbers that may change.