---
name: jobadder-oauth-app
description: Registers a JobAdder partner/developer account and an application to obtain OAuth2 client ID and client secret — covering the approval gate that blocks credentials until JobAdder says yes, authorised redirect URIs, the per-object scope list and `offline_access`, rotating refresh tokens, and the per-account API base URL returned in the token response. Use when asked to get JobAdder OAuth credentials, register a JobAdder developer or partner account, create a JobAdder API application, rotate a JobAdder client secret, or fix a JobAdder auth error like `invalid_grant`, `invalid_request`, a denied authorization, or API calls 404ing against the wrong region host. For any other vendor's developer portal, use that vendor's skill instead.
---

# JobAdder OAuth2 App Registration

**JobAdder credentials are not self-serve.** There is a public registration form, but it is an application to
become a JobAdder API partner, not a signup: JobAdder reviews it, and the client ID and secret you eventually see
do not work until *both* the developer account **and** each individual application have been approved by JobAdder.
Plan for a wait measured in business days, not minutes, and do not promise anyone working credentials the same day.
§2 is the heart of this skill.

Three other things cost real time here. **The API base URL is returned per account in the token response** and must
be stored per connection — JobAdder runs the API on several regional clusters, so a client that hardcodes
`api.jobadder.com/v2` authenticates fine and then fails against every account that does not live there.
**Refresh tokens rotate on every refresh and expire after two weeks of disuse**, so an idle connection dies quietly.
And **which scopes you are allowed to request is decided from what you wrote on the registration form** — the
partner-only scopes are not handed out because you asked for them in an authorize URL.

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **Company name and website** | The registration form is a partner application; these are what JobAdder reviews (§2) |
| **Contact first/last name, email, phone** | Required on the form; the approval email goes to this address |
| **Integration type** | Picked from JobAdder's fixed dropdown — see §2 for the list |
| **Description of the integration** | Free text on the form. This is what determines which scopes you get (§5) |
| **Application name** | Shown to the JobAdder user on the consent screen |
| **Redirect URIs** | Every callback host, exact (§4) |
| **Scope set** | The objects the connector actually reads/writes (§5) |
| **New app, or an edit/rotation of an existing one?** | (§1) |
| **Is a separate test application wanted?** | JobAdder expects one account with several apps, not several accounts (§3) |

## Quick Start

1. Confirm a **new** application is needed — a new client ID orphans every existing customer connection (§1).
2. Apply for the JobAdder developer/partner account and wait for approval (§2).
3. Register the application in the developer portal and wait for *its* approval (§3).
4. Add **every** authorised redirect URI, exactly (§4).
5. Choose the narrow per-object scopes, and always include `offline_access` (§5).
6. Capture the client ID and secret from the application (§6).
7. Wire up the token lifetimes and the **per-account API base URL** from the token response (§7).
8. Verify with a real authorize → callback → refresh round trip against a JobAdder account, not the developer login (§8).
9. Hand the credentials over — never commit them (§9).

## Platform state (verified 2026-09-20 — re-verify before trusting)

- **A developer portal exists at `https://developers.jobadder.com/`** with "Register as a partner" and a sign-in
  page. Historically JobAdder issued credentials only by request to their API/partner team; the form is now the
  front door, but it has not made the process self-serve — it submits an application that a human reviews.
- **Two approval gates, not one.** JobAdder's own help content is explicit: *"Once your Developer Account has been
  approved, you will receive an email to finish activating this"*, and then *"once this has been done and the
  Application has been approved, you will be able to access your Client ID and Secret"*. Treat an unapproved
  application's credentials as non-working, not as a bug to debug.
- **Scope accessibility is decided at registration.** JobAdder states that *"accessibility will be determined based
  on the information provided when registering for a developer account"* and asks for correct, factual information
  about the solution. The partner scopes (`partner_jobboard`, `partner_ui_action`) sit behind this.
- **The API Terms reserve broad control**: JobAdder can revoke or change any partner's access at any time and for
  any reason, and requires **written approval before you publish any partner service**. Publishing or listing is a
  separate business step from getting credentials.
- **OAuth endpoints are stable**: authorize at `https://id.jobadder.com/connect/authorize`, token at
  `https://id.jobadder.com/connect/token`. The identity server's discovery document advertises `S256` PKCE and the
  `client_credentials` and `password` grants, but **JobAdder documents only the authorization-code flow with a
  client secret** — do not build on the undocumented ones without asking JobAdder first.
- **One developer account per partner.** JobAdder asks partners to register exactly one developer account, use its
  application credentials for all customers, and *not* to ask JobAdder users to register their own.

If the portal does not look like this, stop and report what you actually see rather than clicking on.

## 1. Decide: reuse the existing app, or register a new one

A new application means a **new client ID, and every existing customer connection is bound to the old one** — every
JobAdder account would have to re-authorize. Reuse the existing application for: adding a redirect URI, changing the
scope set, rotating a leaked secret, or diagnosing an authorization failure.

Register a **new** application only when the user explicitly wants one: a deliberate test/live split (JobAdder
expects this and supports many applications under one developer account), a separate product surface, or a
replacement for a compromised app. Because each new app carries its own approval wait (§3), say which path you are
taking before you touch the portal.

## 2. Getting access to credentials — the approval gate

This is the step that decides the timeline. There is nothing to click that shortcuts it.

**Apply.** `https://developers.jobadder.com/register` ("Register as a partner"). The form asks for:

- First name, last name, email address, phone number
- Company name, company website
- **Integration type** — a fixed dropdown. As of 2026-09-20 the options are: Background Check Software, Benefits
  Administration Software, Compensation Management Software, Employee Engagement Software, Employee Goal Setting
  Software, Employee Pulse Survey Tools, Employee Recognition Software, Employee Scheduling Software, HR Analytics
  Software, Human Resource Apps, Learning Management Systems, Mentoring Software, Messaging Software, OKR Software,
  Onboarding Software, Online Training Software, Payroll Software, Performance Management Software, Pre-Employment
  Assessment Tools, Reference Checking, Talent Management, Time and Attendance Software, Vacation/Leave Tracking
  Software, Video Interview Software, Voice Call Software, Workforce Management Software, 360 Degree Feedback
  Software, Other.
- **"Briefly describe your integration plans"** — free text, and the highest-leverage field on the form. JobAdder
  decides scope accessibility from it. Name the objects you will read and write (candidates, job applications,
  jobs, companies, contacts, users) and say whether you need job-board or partner-action access. A vague answer
  buys a vague grant and another round trip.
- Agreement to the **JobAdder API Terms** (`https://jobadder.com/api-terms`).

**Then wait.** Approval arrives as an activation email to the address on the form. If it does not arrive, the
follow-up is with JobAdder's API/partner team — chasing it is the user's call, not something to automate.

**A partnership request is a different thing.** `https://jobadder.com/partnership-request/` is the commercial
marketplace/partner-ecosystem application. It does not issue credentials, and credentials do not get you listed.
If the user wants both, they are two tracks; do not conflate them in your summary.

**What only a human can do**, and where to hand back rather than loop: submitting the form, accepting the API
Terms, verifying the email, activating the account, and any correspondence with JobAdder about approval or scope.

**If this session has no browser automation** (the usual case for a CLI or cloud run), do not pretend to click.
Give the user an exact, ordered path with the literal values to paste — the integration-plans text, the redirect
URIs from §4, the scope list from §5 — and continue once they report back with the client ID.

## 3. Register the application

Once the developer account is active: sign in at `https://developers.jobadder.com/signin` and register a new
application. What it needs:

- **Application name** — this is what the JobAdder user sees on the consent screen. Use the product name the
  customer will recognise, not an internal codename.
- **Authorised redirect URIs** — see §4. These must be entered here before they will work.
- Supporting URLs (such as a privacy policy) if the form asks; confirm the current fields in the portal rather than
  assuming this skill's list is complete.

Then **wait for the application to be approved**. Credentials exist on the page before approval; they do not work
before approval. A brand-new client ID failing at the authorize step is the expected behaviour of an unapproved
app, not a misconfiguration to hunt.

**Test vs live.** JobAdder's guidance is one developer account with as many applications as you need, typically one
test and one live, each with its own client ID and secret. There is a separate sandbox API host
(`https://sandboxapi.jobadder.com/v2`) and JobAdder operates a Sandbox cluster alongside the production regional
ones — but you never choose that host yourself, because the token response tells you which base URL to use (§7).
Ask JobAdder for sandbox/test-account access as part of the registration conversation; it is not a checkbox in the
portal.

## 4. Redirect URIs

Register **every** callback host your platform serves, in the application's Authorised Redirect URI list. JobAdder
matches the `redirect_uri` you send against that list, and the same value must be sent again at the token exchange.

For Unified.to these are one per data center; confirm the current list with the platform owner rather than
assuming:

```
https://api.unified.to/oauth/code          # us (default)
https://api-eu.unified.to/oauth/code       # eu
https://api-au.unified.to/oauth/code       # au
https://api-dev.unified.to/oauth/code      # dev
```

Notes:

- Enter the **full and exact** URI, scheme included. JobAdder's own note is explicit about this. No subdirectory
  matching, no trailing-slash forgiveness.
- The `redirect_uri` sent at the **token exchange** must equal the one sent at **authorize**. Mismatch there
  produces a failed exchange, not a failed redirect, which is why it gets misdiagnosed as a bad secret.
- These are *your* callback hosts. They do not vary per JobAdder account, even though the API base URL does (§7).

## 5. Scopes

Scopes are **space-delimited** in the authorize URL. JobAdder splits them per object, with `read`/`write` as
blunt catch-alls.

| Scope | Grants |
| --- | --- |
| `read` / `write` | Almost all GET / almost all POST+PUT endpoints. JobAdder explicitly advises **against** these for privacy and security; request the narrow ones instead |
| `read_candidate` / `write_candidate` | Candidates |
| `read_jobapplication` / `write_jobapplication` | Job applications |
| `read_job` / `write_job`, `read_jobad` / `write_jobad` | Jobs and job ads (separate scopes — a job list without ads needs both) |
| `read_company` / `write_company`, `read_contact` / `write_contact` | Companies and contacts |
| `read_events` / `write_events` | Scheduled events (meetings/interviews) |
| `read_interview` / `write_interview` | Job interviews and evaluations — distinct from `read_events` |
| `read_opportunity` / `write_opportunity` | Opportunities (pipeline/deals) |
| `read_note` / `write_note`, and per-object `*_note` scopes | Notes: candidate, company, contact, job, job application, placement, opportunity, requisition |
| `manage_*_custom` | Custom fields, per object |
| `read_placement` / `write_placement`, `read_requisition`, `read_submission`, `read_float`, `read_folder` / `write_folder`, `read_usertask`, `write_sms` | The rest of the catalogue |
| `read_user`, `read_usergroup` | Users and user groups — needed by almost everything that attributes a record to a consultant |
| `partner_jobboard`, `partner_ui_action` | **Partner-only.** Job-board posting/apply, and partner action buttons inside JobAdder. Gated on what you declared at registration (§2) |
| `offline_access` | **Required for a refresh token at all.** Must always be combined with other scopes |

Rules worth internalising:

- **No `offline_access`, no refresh token.** The token response simply omits it, and the connection dies in an hour.
- **Webhooks require `offline_access`** plus the scopes relevant to each event (`candidate_updated` needs
  `read` or `read_candidate`, and so on). A webhook you cannot subscribe to is usually a missing event scope.
- Scopes never exceed what the authorizing JobAdder user may do. Which brings us to the one that surprises people:
  **only an admin user, or a user with "Grant API access to Integration partners", can authorize your app.** A
  standard consultant clicking your connect button will be refused, and that is a customer-side permission fix.
- **Do not authorize with the developer-account login.** The user who authorizes must be a user of the *customer's*
  JobAdder account. This is a documented mix-up, and it produces a connection to the wrong data.

### As of 2026-09-20, Unified.to's JobAdder connector requests

Across its ATS, CRM, HRIS, metadata and login surfaces, the connector's scope set is:
`read`, `read_user`, `read_usergroup`, `read_candidate`, `write_candidate`, `read_jobapplication`,
`write_jobapplication`, `read_job`, `write_job`, `read_jobad`, `read_company`, `write_company`, `read_contact`,
`write_contact`, `read_events`, `write_events`, `read_interview`, `read_opportunity`, `write_opportunity`,
`read_note`, `write_candidate_note`, `read_contact_note`, `write_contact_note`, and `offline_access` on every
non-login operation. The login/identity path requests only `read` and `read_user`.

Auth quirks of that connector, same date: it sends `prompt=select_account consent` on authorize (so a user with
several JobAdder accounts is asked which one, every time); it posts the token and refresh requests as form-encoded
with the client ID and secret in the body; **it reads the API base URL out of the `api` field of the token response
and stores it per connection**, rather than using the production or sandbox host it lists as defaults; and it ships
**no shared Unified.to JobAdder credentials** — every workspace must supply its own client ID and secret, which is
the reason this skill exists. Custom-field/metadata reads go through the ordinary `read_*` object scopes, not
`manage_*_custom`. Automatic new-connection testing is turned off for this connector.

Note the surface split: **`jobadderassessment` is a separate connector** covering JobAdder's assessment/partner-action
surface, and it requests a different, partner-gated scope set built around `partner_ui_action`. If the user's request
is about assessments or partner action buttons, they may need those scopes approved at registration (§2) — but that
connector is out of scope here. Confirm the current scope set and auth behaviour with the connector's owner before
submitting anything; this is a dated snapshot, not a contract.

## 6. Capture the credentials

From the developer portal: sign in → your application → the client ID and secret are shown on the application. They
remain readable from the developer account afterwards, so this is not a shown-once secret — but treat it as one
anyway and do not screenshot it into a ticket.

Capture:

- **Client ID** and **client secret**
- Authorize URL `https://id.jobadder.com/connect/authorize` and token URL `https://id.jobadder.com/connect/token`
  (the same pair for every account and every region)
- Which application this is — test or live
- The exact authorised redirect URI list you registered

Rotating the secret invalidates the old one. Existing access tokens live out their hour, but every token exchange
and refresh fails until the new secret is deployed — and since refresh tokens rotate (§7), a failed refresh window
can cost you the refresh token as well. Never rotate without explicit go-ahead and a cutover plan.

Report the secret once so the user can paste it into their secret store, say plainly that it is now in the
transcript and can be rotated, then move on.

## 7. Token lifetimes and the per-account API base URL

The token response is where JobAdder differs most from other providers:

```json
{
  "access_token": "...",
  "expires_in": 3600,
  "token_type": "Bearer",
  "refresh_token": "...",
  "api": "https://<region-cluster>.jobadder.com/v2",
  "instance": "...",
  "account": 12345
}
```

| Thing | Behaviour |
| --- | --- |
| **Authorization code** | Valid **5 minutes**, single use. Exchange it immediately on the callback |
| **Access token** | **60 minutes** (`expires_in: 3600`) |
| **Refresh token** | Only issued with `offline_access`. **Expires after 2 weeks unused**; each use resets the 2-week timer |
| **Refresh rotation** | Every refresh returns a **new** refresh token. Store it. A client that keeps replaying the original fails on the second refresh |
| **`api`** | **The API base URL for this account.** Store it per connection and use it for every subsequent call |
| **`instance` / `account`** | Identify the JobAdder account behind the token — map them to your customer record so you can tell two connections apart |

**The `api` rule is the classic failure.** JobAdder runs the API on several regional clusters (its release notes
name AU, EU, US and Sandbox instances). `https://api.jobadder.com/v2` is a default, not a universal host. A client
that hardcodes it authenticates successfully and then 401s or 404s on every data call for accounts on another
cluster — a bug that looks like bad scopes and is not. The base URL is discovered from the token response; there is
no separate lookup call and nothing to derive from the account name.

Two more operational facts:

- **Deleted users kill connections.** If the JobAdder user who authorized is deleted (they left the company), the
  refresh token starts returning `invalid_grant` / `invalid_request`. That is a re-authorize by another admin, not
  a retryable error — handle it as a connection-level failure, not a transient one.
- **Throttling is per JobAdder account.** A 429 comes with a `Retry-After` header in seconds; stop calling that
  endpoint for that account until it elapses, and spread requests out rather than bursting.

## 8. Verify end-to-end

Authorizing with your developer-portal login proves nothing. Test the path a customer takes:

1. Authorize from a **real JobAdder account**, signed in as an admin (or a user holding "Grant API access to
   Integration partners"), through your platform's actual connect flow.
2. Confirm the token response carries a **`refresh_token`** — if it does not, `offline_access` was missing.
3. Confirm your client stored **`api`**, `instance` and `account`, and that the first data call goes to the `api`
   host rather than a hardcoded one.
4. Force a **refresh**, then force a **second** refresh using the token the first one returned. That is what
   catches a client ignoring rotation.
5. If webhooks are in play, create one and confirm a delivery — and note that a webhook suspends itself after 10
   failed deliveries in an hour, losing every event until it is re-enabled by an update call.

| Symptom | Cause |
| --- | --- |
| Authorize page errors, or the app is rejected outright, on a brand-new client ID | The application (or the developer account) is not approved yet (§2, §3) |
| Redirect comes back with `error=access_denied` | The user declined — or is not an admin / lacks "Grant API access to Integration partners" (§5) |
| Token exchange fails although the redirect worked | `redirect_uri` at exchange differs from the one sent at authorize, or is not in the authorised list (§4) |
| Token exchange fails on a code that looked fine | Code older than 5 minutes, or already used once (§7) |
| No `refresh_token` in the response | `offline_access` not requested (§5) |
| Second refresh fails | Client is not storing the rotated refresh token (§7) |
| Connection dies after a quiet fortnight | Refresh token expired from 2 weeks of disuse (§7) |
| `invalid_grant` / `invalid_request` from one customer only | The authorizing user was deleted — needs a fresh authorization (§7) |
| Auth succeeds, every API call 401s or 404s | Client ignored `api` and called the wrong regional host (§7) |
| A scope is refused no matter how it is spelled | Partner-gated scope, or one not granted from your registration description (§2, §5) |
| Sudden 429s | Per-account throttling; honour `Retry-After` (§7) |
| Webhook silently stops delivering | Suspended after 10 failures in an hour; re-enable via an update call (§8) |

## 9. Hand off — never commit the secret

- **Do not** write the client secret into source control, a test, a fixture, a committed `.env`, a ticket, a PR
  body, or a chat channel. Values go to the user, for the secret store or console.
- If a code change is needed (a redirect host, a scope, storing the per-account base URL), keep it secret-free and
  say what the human must set out of band.
- Close with: the developer account owner and application name; approval status of **both** gates; the client ID;
  where the secret was delivered; the authorize and token URLs; the exact scope strings requested and why; the
  registered redirect URIs; confirmation that the client stores `api`, `instance`, `account` and rotated refresh
  tokens; and anything still waiting on JobAdder.

## Stop and ask

Hand back to a human rather than guessing when: the developer account or application is still unapproved and
someone wants a workaround; JobAdder asks for commercial, legal, volume or compliance detail on the partner
application; the integration needs `partner_jobboard` or `partner_ui_action` and those were not declared at
registration; the client does not store rotated refresh tokens or the per-account `api` base URL (report it — do
not register and hope); a customer cannot authorize because no admin will, or because their user was deleted;
someone proposes using the undocumented `client_credentials` or `password` grants; a marketplace listing or
published partner service is wanted (JobAdder requires written approval first); or the portal does not match the
**Platform state** section above.

## References

Every URL below returned 200 on 2026-09-20.

- JobAdder Developers portal — https://developers.jobadder.com/
- Register as a partner (developer account application) — https://developers.jobadder.com/register
- Developer portal sign-in — https://developers.jobadder.com/signin
- API Reference (interactive) — https://api.jobadder.com/v2/docs
- **OpenAPI document** — https://api.jobadder.com/v2/openapi.json — the authoritative source for the OAuth flow,
  the full scope list, the webhook event/scope matrix and the release notes
- OpenID Connect discovery (endpoints, scopes, PKCE and grant support) — https://id.jobadder.com/.well-known/openid-configuration
- JobAdder API Terms — https://jobadder.com/api-terms
- Partnership request (commercial partner track, **not** credentials) — https://jobadder.com/partnership-request/

JobAdder's help centre (`jobadderapi.zendesk.com`) serves its `/hc/` article pages behind a bot check and returns
403 to automated fetches; they open normally in a browser. The identical article content is served — and verified
200 — at these JSON endpoints:

- OAuth2 Authentication — https://jobadderapi.zendesk.com/api/v2/help_center/en-us/articles/360022196774.json
- Developer Account Applications — https://jobadderapi.zendesk.com/api/v2/help_center/en-us/articles/360023091673.json
- Redirect URIs — https://jobadderapi.zendesk.com/api/v2/help_center/en-us/articles/360022835513.json
- Partner Tech Integration — https://jobadderapi.zendesk.com/api/v2/help_center/en-us/articles/7040444063503.json
- Webhooks — https://jobadderapi.zendesk.com/api/v2/help_center/en-us/articles/360022511513.json
- API Throttling — https://jobadderapi.zendesk.com/api/v2/help_center/en-us/articles/4410850130713.json
- Authorization Denied — https://jobadderapi.zendesk.com/api/v2/help_center/en-us/articles/360022474453.json
