OrgOrg API v1
The OrgOrg REST API lets you read your org directory and org chart, manage user accounts, read and write go links, page oncall, work with your CRM and goals, and send thanks. Endpoints are stable; new fields may be added without notice.
Base URL
https://orgorg.comAuthentication
All endpoints require a bearer token in the Authorization header. Tokens are issued per-user and inherit that user's permissions in your organization.
curl https://orgorg.com/api/v1/auth/test \
-H "Authorization: Bearer YOUR_API_TOKEN"Generate a token from the API credentials page inside your OrgOrg account.
Profiles and users are two different things
People data lives in two resources, and knowing which one you want saves a lot of confusion:
- Profiles is your directory and org chart: names, titles, departments, teams, locations, employment start dates, and manager relationships. It needs the
profile:readscope, which any member can hold, because everything on it is already visible to everyone in the app. It defaults to active people only, so a count of the response is a true headcount — someone invited but not yet joined does not inflate it. Pass?status=Active,Pendingif you want both. - Users is account state: account type, status, and the invite, join, and last-seen timestamps. It needs
user:read, which only an administrator can grant, because members cannot see this in the app. Unlike profiles it is not filtered to active people, which is what makes attrition analysis possible.
The two join on userId. Pull both and join once and you have the full picture.
Two fields are easy to mix up on purpose: startDate on a profile is when someone started working here, and joined on a user is when their OrgOrg account was created. If your org has never set start dates from an HRIS, startDate mirrors account creation, which is worth a spot check before you build tenure logic on it.
Syncing to a warehouse
Both list endpoints page with an opaque cursor and support incremental sync. Walk once to seed, then pull only what changed.
# 1. Seed: walk to exhaustion, following nextCursor.
curl "https://orgorg.com/api/v1/profiles?limit=500" -H "Authorization: Bearer $TOKEN"
# -> { data: [...], removed: [], nextCursor: "...", hasMore: true,
# syncedThrough: "2026-07-31T02:18:51.391Z" }
# 2. Store the LAST syncedThrough you saw, then on the next run:
curl "https://orgorg.com/api/v1/profiles?updatedSince=2026-07-31T02:18:51.391Z" -H "Authorization: Bearer $TOKEN"
# -> { data: [changed people], removed: [ids who left], ... }
# 3. Apply: upsert everything in "data", delete/flag everything in "removed".Do not skip the removed array. When someone is deactivated they leave the directory, so they stop appearing in data entirely. On a full walk you would notice by their absence, but on an incremental pull there is nothing to notice — which is exactly why removed exists. It is populated only when you pass updatedSince. Ids you have never seen are safe to ignore.
On /api/v1/users no such array is needed: that endpoint is not filtered by status, so a departure simply shows up as a row with accountStatus of Inactive.
To rebuild the org chart, self-join the directory on managerId, which is in the same id space as id. Use a LEFT join: a manager who is hidden from the directory is still recorded as someone's manager, so that managerId will not resolve to a row. Those people still carry managerEmail, so you can identify the manager even when the id does not match.
SELECT p.id, p.displayName, p.department, m.displayName AS manager
FROM profiles p
LEFT JOIN profiles m ON p.managerId = m.id; -- LEFT, not INNERTimestamps
Every timestamp is an ISO 8601 string in UTC, so it is readable without a converter and carries its zone explicitly. Where a value came from an HRIS sync it lands on midnight UTC (2024-11-04T00:00:00.000Z); a mid-day value on startDate means the org has never set one and it fell back to when the profile was created, which is worth knowing before you build tenure logic on it.
updatedSince accepts either an ISO 8601 string — pass back the syncedThrough you were given — or raw epoch milliseconds.
Rate limits
60 requests per minute per token, with a burst allowance of 120. Past that you get a 429 with a Retry-After header giving the seconds to wait.
For scale: a 10,000-person org is roughly 20 requests for a complete directory pull at the maximum page size, so a weekly or even hourly refresh sits comfortably inside the limit. If you need more headroom, get in touch and tell us the shape of the job.
For AI agents
The entire API — authentication, every endpoint, parameters, example requests, and response shapes — is available as a single markdown document at /docs/api/v1/agents.md. Paste it into an agent's context, or have the agent fetch it directly:
curl https://orgorg.com/docs/api/v1/agents.md