Skip to content

Authentication

Requests to /open/v1 use a Bearer credential: a personal API key for your own account, or an OAuth access token when an app acts for a user.

Who Credential Represents
Your own account Personal API key from the personal console You, within the scopes selected for the key
A person using your app OAuth access token (authorization code + PKCE) The user, with your app’s client_id attached
Your server App API key (issued in the developer console) Your app itself — server-to-server calls such as reading usage

Personal keys and user OAuth tokens can access agent configuration and conversation endpoints within their granted permissions. When a user first authorizes your app, the platform adds them to the app’s user list in the developer console.

Open Personal API keys to create a key. You do not need a merchant account or OAuth client. Available scopes are profile.read, role.read, role.write and chat.play; choose only those your tool needs.

The secret is shown once. Keep it private and send it as Authorization: Bearer <personal API key>. Personal keys cannot administer the console. Revoke a key from the same page when it is no longer needed. An expired or revoked key cannot be refreshed: create a replacement when needed.

Register once per origin with POST /oauth/register. Rules:

  • redirect_uris must be https, except http://localhost and loopback IPs during development.
  • A redirect URI must not contain a fragment. Use history routing, not hash routing, for the callback page.
  • Persist the returned client_id keyed by origin. Registering again creates another client.
  • The registration body accepts a space-separated scope string, for example "profile.read role.read role.write". Register only the maximum access your app needs. Omitting it defaults to profile.read role.read.
  • Add client_name#<lang> keys (zh-Hant, zh-Hans, en, ja, ko) to show a different app name to people signing in with that interface language; client_name remains the name for every other language.

Send the browser to /oauth/authorize with response_type=code, your client_id, the redirect_uri, a state, a PKCE code_challenge (S256) and resource=https://api.harperharbor.com/open/v1.

Include a space-separated scope query parameter, URL-encoded, for example scope=profile.read%20role.read. Requested scopes must be a subset of those registered for the client. Omitting this parameter requests all of the client’s registered scopes, so pass an explicit subset for each workflow.

The server signs the user in, asks for consent, and redirects back with ?code=…&state=…. Verify state before exchanging the code.

Your app can add prompt=none to the same authorization request to reuse an existing sign-in and grant without showing a sign-in or consent page. Use a top-level browser navigation to the absolute API authorization endpoint, with a fresh state and PKCE challenge for each attempt. Do not use an iframe or fetch.

The API validates the client and registered redirect URI before redirecting through the Console’s same-origin OAuth endpoint, where the browser’s existing HarperHarbor sign-in cookie is available. Your app must allow this intermediate top-level redirect; it does not receive or share the sign-in cookie.

Silent authorization succeeds only when the user is signed in, already belongs to the client’s app, and has previously granted every requested scope to that client. It returns the usual authorization code and original state. It never creates an app membership or records new consent. An external app must also check its own membership records before creating a local session.

When interaction is needed, the registered callback receives the original state and an error instead of a code:

Error Next step
login_required Show your app’s sign-in option
consent_required Offer an explicit sign-in or join action to start the normal consent flow
interaction_required Offer the normal authorization flow when the user chooses to continue

Validate state on error callbacks as well as successful callbacks. Keep silent attempts bounded and return to your app when they fail; do not restart them in a redirect loop. A missing, expired or revoked HarperHarbor sign-in session cannot be silently recovered. Invalid clients, unregistered redirect URIs and invalid authorization parameters return HTTP 400 directly without redirecting to the callback. none cannot be combined with another prompt value.

POST /oauth/token with grant_type=authorization_code, the code, client_id, redirect_uri, code_verifier and the same resource. You receive access_token, refresh_token and expires_in.

Refresh with grant_type=refresh_token. A 401 response means authentication failed. Try refreshing the token. If refresh also fails, ask the user to authorize again.

Request only the scopes your integration needs:

Scope Access
profile.read Read the public account identifier
profile.write Set the user’s nickname in your app, for example when your app is where they choose a display name. Nicknames are kept per app; other apps keep their own. Personal API keys cannot carry it
role.read Read agent configurations and Lorebooks, including the authorized user’s private content
role.write Create and edit agent configurations and Lorebooks, request configuration publication, and upload or delete hosted images
chat.play Start agent conversations that consume the user’s credits
referral Read the user’s referral code and results, and enter a referral code for a newly registered user. Ask the user before entering a code: the binding is permanent

The console scope is reserved for the platform console. Third-party applications cannot request it. Scope identifiers are part of the API contract; the product label “Agent configuration” does not introduce an agent.write scope.

A user can revoke your app from their account page at any time. Existing tokens stop working immediately; your next call returns 401. Handle it like an expired token: try to refresh, then send the user back through authorization.