Skip to content

Connect an app

Register your own Sign in with Tahoe app, and who may approve what.

An app of your own can let your recruiters sign in with their Tahoe accounts, read what they can read and, when you allow it, change things for them. This guide is for the owner or admin who registers the app, and for the developer who builds the sign-in. The sign-in itself is the standard flow on Sign in with Tahoe. What is different for an app that your own workspace registers is who may register it, which scopes it may ask for, and who may approve it.

Register the app

Open the OAuth apps section

In Settings, under Developer. Only owners and admins of the workspace can see it and register an app. A workspace can register a limited number of active apps, 5 by default. Disable an app you no longer use to make room.

Describe the app

  • Name, 2 to 80 characters. It is what a recruiter reads on the consent screen.
  • Redirect URIs, 1 to 5. Each must be an absolute https URL with a host, no user name or password and no fragment. A plain http address is refused, including http://localhost, so use a development host that has a certificate, or a secure tunnel. They are matched exactly when someone signs in.
  • Scopes. See below. openid and offline_access are always included.

An app you register is confidential: it has a client secret and keeps it on a server. A browser or mobile app that cannot keep a secret is not offered. For that, ask Tahoe.

Copy the client secret

Tahoe shows the client_secret once, with the client_id (it starts with thc_). It stores only a hash and cannot show the secret again. Put it in your secret manager before you close the dialog.

Build the sign-in

Follow Sign in with Tahoe: discovery, PKCE with S256, the code exchange and the refresh. Ask for the scopes you registered, and no others, because a scope the app is not registered for is refused.

Authorization request, with a write scope
https://tahoe.workonward.com/api/partner/v1/oauth/authorize
  ?client_id=thc_7f3a9c21d84e4b6fa0c5e19b2d7a6e31
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fauth%2Ftahoe%2Fcallback
  &response_type=code
  &scope=openid%20offline_access%20applications%3Aread%20applications%3Awrite
  &state=<random, tied to the user's session>
  &nonce=<random, saved for the id_token check>
  &code_challenge=<base64url(sha256(code_verifier))>
  &code_challenge_method=S256

Rotating the client secret

Choose rotate on the app to get a new secret. The old secret keeps working until the next rotation, so you can deploy the new one without an outage: deploy it, confirm it works, and the old one stops working only when you rotate again. Rotate on a schedule, and at once if a secret may have leaked.

Disabling an app

Disabling an app ends every user’s consent to it and revokes every refresh token it holds, for every user. Their access tokens stop working on the next request. It cannot be undone, so register a new app if you need one again.

Scopes an app may ask for

An app registered by a workspace follows the same tiers as that workspace’s API keys. See Which scopes you can give a key.

GroupWhat it means for an app
IncludedAny owner or admin may register an app for these.
Paid planThe workspace needs an active subscription. This includes every scope that writes.
Not availableNever offered to an app a customer registers: the shared pool, people matching, sourced profiles, attachments and the raw text of a resume.

A write scope needs one more thing: writes for connected apps must be switched on. Until they are, the scope is refused when you register the app and cannot be granted on the consent screen.

Who can approve what

The user who signs in sees the consent screen and decides. A customer-registered app is shown there as unverified, with the name of the workspace that registered it, because Tahoe has not reviewed it. A scope the user may not grant is shown unticked, with the reason. The same rules are checked again when the user approves, so an edited form cannot grant more.

RuleDetail
Only its own workspaceAn app a customer registered can be approved only for the workspace that registered it. For any other workspace, the user sees that the app is for another workspace.
Owner or admin only, for writes and sensitive dataA member or viewer can approve an app that reads their ordinary data, but not one that writes, and not one that reads personal data such as contact details or resumes.
A paid plan for paid scopesA paid plan scope, and every write scope, needs a workspace with an active subscription.
Never staff scopesThe scopes under Not available cannot be granted to a customer-registered app by anyone.
Writes switched onA write scope can be granted only when writes for connected apps are switched on.

Where an approved app acts

A token reads the workspace the user was signed in to when they approved. If the token carries a write scope or a sensitive scope, it acts only in workspaces where the user is still an owner or admin. Demote the user, and the app keeps its read access to ordinary data and loses the rest on the user’s next request. Remove the user from the workspace, and everything ends, including the next refresh. A user can also disconnect the app at any time in Settings, under Connected apps.

Writing as the user

When writes for connected apps are switched on, a token that holds a write scope can use the same write endpoints an API key can. Until then every write returns 403 write_requires_api_key.

403 Forbidden, while writes are off
{
  "detail": {
    "code": "write_requires_api_key",
    "type": "permission",
    "message": "Writing requires an API key. A Sign in with Tahoe token may only read.",
    "param": null
  }
}
  • The write is the user’s, never the app’s. A note, a stage move or a message appears in Tahoe under the name of the person who signed in, and the activity entry that marks it as coming from the API holds the app and the request. What the user could not do in Tahoe, the app cannot do.
  • Everything else is as for a key. The same endpoints, the same scopes, the same errors, and the same rules about candidates: nothing is sent to a candidate except through POST /messages, and nothing is deleted. See Scopes that write.
  • Send an Idempotency-Key. Your app retries, and so does a user who double-clicks. See Idempotency.
  • Events carry an origin. A change your app makes produces events with an origin that begins with app: followed by your client ID, so you can skip your own echo. See The origin of an event.
A write with a token from Sign in with Tahoe
curl -X POST https://tahoe.workonward.com/api/partner/v1/applications/app_6Qm2xKd4Rp8v/move \
  -H "Authorization: Bearer <access_token>" \
  -H "Idempotency-Key: move-6Qm2-to-screen-0001" \
  -H "Content-Type: application/json" \
  -d '{ "stage_id": "stg_3Rp8vKd2mXq4" }'

Refresh tokens and rotation

  • Every refresh returns a new refresh token and ends the one you sent. Save the new one before you use it.
  • A refresh token expires after 30 days without use, and the chain from one sign-in ends 90 days after that sign-in.
  • Sending a refresh token that was already exchanged ends every token from that sign-in, and the user signs in again. Refresh one user at a time, and never retry with the old token after a timeout.
  • Disabling the app, a password reset, and the user leaving the workspace all end the tokens. Treat invalid_grant as “send this user through sign-in again”.