Acting for a customer

Provisioning gives you an account. This is how you operate it.

An account you provisioned has no human attached, so there is nobody who can sign into our dashboard to configure a character or issue a conversation credential. The connected-app credential closes that: two headers on a FluidTalk call, and you act as the owner of that account.

POST https://api-talk.fluidvip.com/api/strategy/…
X-Partner-Key:     fv_pk_…      which app is asking
X-Partner-Account: 9c1f4b7e-…   which account it is asking about

Both headers, every time. A partner key on its own does not say which of your accounts you mean, and there is no default. The account is the owner_id you were given at provision time.

Why two headers and not a token

Your key is the same one the account plane uses, and it stays on your server. The account id is not a secret — it is derived under a salt you do not hold and it names one of your own customers — so naming it in a header costs nothing and means there is no second credential to mint, store, rotate or leak.

FluidTalk does not decide whether you are allowed. It asks FluidAccount, on every request:

FluidTalk ──X-Service-Key──> POST /api/partner-grant/verify   {partner_key, owner_id}
                             ├─ {"authorised": true,  "partner_id": "…", "partner_slug": "…"}
                             └─ {"authorised": false, "reason": "unknown_key|suspended|no_grant|revoked"}

The reason never reaches you — all four answer identically. FluidAccount owns the partner record and the grant, so it owns the answer; FluidTalk holds no copy of the grant table that could go stale against it.

::: warning There is no cached "last good" answer. If FluidAccount cannot be reached, the request is refused — it does not fall back to a previous yes. A cache of last-good answers would mean a revoked partner keeps access for the length of an outage, and a revocation your customer just performed is exactly the thing that must not be survivable. Expect 503 partner_auth_unavailable during an outage rather than degraded access. :::

What you become

The context resolves to the owner of that one account. In practice:

Every character, engine, binding and connector-token route filters on that account
Usage you generate billed to the customer's wallet, at their plan's rate — never yours
Your identity in that request a synthetic partner:<id>, not a person

That last row is what stops the credential drifting into things it was never meant to reach. Anything keyed on a real human's session cannot match a synthetic id — platform administration and team management both key that way, so neither is reachable, by construction rather than by a denylist someone has to remember to extend.

::: danger The customer pays, not you. Billing attribution is set to the account, not the partner. This is the commercial shape of the whole model — your customers' usage is billed to their own accounts at their own tier. If it were ever set to the partner, every one of your customers' inference bills would silently land on you. :::

The two things you will actually do

1. Configure a character

The ordinary character, engine and binding routes under /api/strategy/ work exactly as they do for a signed-in owner. Nothing about their behaviour changes because a partner asked.

2. Issue a connector token

This is the one that unblocks conversations. A connector token is the credential the Characters API takes, and until now it could only be created by a human in the dashboard.

curl -X POST \
  https://api-talk.fluidvip.com/api/strategy/characters/{character_id}/connector-token \
  -H "X-Partner-Key: fv_pk_…" \
  -H "X-Partner-Account: 9c1f4b7e-2a58-5d03-b6e1-77af0c9d3218"

The token is revealed once, exactly like your partner key — only its hash is stored. Calling again rotates it, which invalidates the previous one. Store it when you receive it.

::: tip Conversations do not use these headers. Once you hold a connector token, you drive /api/v1/characters/* with X-Connector-Token and the partner headers play no part. That separation is structural: the conversation routes do not resolve a partner credential at all, so no message you send depends on FluidAccount being reachable. :::

What you cannot do this way

Stated plainly, because two of these will look like bugs otherwise.

You cannot Why
Reach FluidCloud-backed features The context carries no user assertion that could be replayed to Cloud, so anything requiring a Cloud tenant refuses. Photo features that depend on Cloud are not reachable through a partner context
Manage team members or invitations Keyed on a real human's identity, which a synthetic partner id cannot match
Act as a platform administrator The operator allowlist holds ordinary user ids; a partner id can never be one
Read or spend the account's balance Balance lives behind a different credential — see Billing
Act for an account you did not provision, or one whose owner revoked you 403, and the message never says which

Errors

Status error Meaning
403 Not authorised to act for that account: unknown key, suspended partner, revoked grant, or never connected. One opaque message for all four — the response can never be used to enumerate accounts or keys
503 partner_plane_disabled The partner plane is off on this deployment, or this service is not wired to the authority. Permanent until an operator changes it — do not retry in a loop
503 partner_auth_unavailable The authority could not be reached. Transient; retry
429 Rate limited. Carries Retry-After

Two independent rate limits protect this path: a local per-credential cap that refuses before any cross-service call is made, and the authority's own cap, whose 429 and Retry-After are relayed to you unchanged.

::: warning partner_plane_disabled is not something you can wait out. It means PARTNER_PROVISIONING_ENABLED is off at FluidAccount, or ACCOUNT_API_BASE_URL is unset on the FluidTalk deployment you are pointed at. Both are operator decisions and neither resolves on its own. Ask your Fluid contact which environment you are on rather than retrying. :::