Skip to content

Let an agent call your APIs on a user's behalf ​

This page explains the on-behalf-of (OBO) flow for a custom API and where it differs from the current Qoni SDK product surface.

Current SDK boundary

The named APIs in @qoniai/qoni@0.4.1 cover GUMem and Web Agent and accept only product scopes known to the SDK. The package does not expose a named end-to-end method that accepts an arbitrary custom resource and arbitrary custom scopes.

Do not use QoniScopes.WEB_SEARCH_*, DO_ANYTHING_*, or another Qoni product permission in a “your API” example. Do not present qoni.webSearch.run() as a custom-API call. Configure the custom resource and scopes in GenAuth, then use the HTTP API reference for that protocol flow.

Two different call paths ​

Target resourceRecommended call pathToken handling
Qoni GUMem / Web AgentUse qoni.gumem.*, qoni.webSearch.*, qoni.doAnything.*, qoni.deepResearch.*, or qoni.track.*Pass grant.token to the named method; the SDK performs product token exchange internally
Your custom APIUse the GenAuth HTTP contract and the resource / scopes you configuredExchange for an access token whose audience is your API, then validate it at your resource server

The paths share delegation, audit, and attenuation concepts, but their code and scopes are not interchangeable.

Custom-API OBO flow ​

1. Configure the resource and scopes ​

Register your API resource identifier and delegable scopes in GenAuth. The resource, scope names, and token audience must come from real configuration, not from Qoni Web Agent constants.

2. Start user authorization ​

Your trusted backend creates an interactive delegation through the GenAuth HTTP API. The request includes:

  • A stable Agent identifier.
  • Scopes registered for your API.
  • A registered redirectUri.
  • An anti-replay state generated and stored by your server.

Interactive mode resolves the signed-in user on the authorization page. Do not send the removed task, agentKey, or top-level userId SDK model.

3. Complete the callback ​

After approval, the callback brings back a one-time code, your business state, and grant_state, but no grantId. Look up the record you saved by grant_state and check the business state, then complete the exchange with the saved grantId, the callback code, and grant_state passed as state. Keep the delegation token on a trusted server.

4. Exchange for a custom-API access token ​

Follow the token-exchange contract in the API reference. resource must be your registered API resource, and scopes must be a subset of the scopes actually granted.

Do not use webagent as a placeholder custom resource. That targets Qoni Web Agent, not your service.

5. Validate at the resource server ​

When your API receives Authorization: Bearer <access-token>, it must at least:

  1. Verify the signature with trusted JWKS.
  2. Validate iss, aud, exp, and nbf.
  3. Identify the represented user from sub and apply business authorization.
  4. Identify the acting Agent from act and record it in audit logs.
  5. Verify that scopes cover the current operation.
  6. Reject requests whose audience, user, Agent, or scopes do not match.

See Protect your APIs for the resource-side checklist and Token and claim reference for claim shapes.

Acceptance checks ​

  • A read-only token must be rejected by a write endpoint.
  • The access-token aud must identify your API, not webagent or another Qoni product.
  • sub, act, scopes, grant ID, and audit ID must join into one audit chain.
  • After revocation, later exchanges or calls fail according to GenAuth revocation policy.

If you only need a Qoni product ​

Do not use the custom-API path above. After delegateToken(), pass grant.token directly to the named product method:

ts
const search = await qoni.webSearch.run({
  token: grant.token,
  prompt: 'Qoni Agent Identity',
})

const result = await search.wait()

Next steps ​