Create a position
Makes a position: a seat in your organisation with its department, site, grade and the position it reports to.
It's vacant until someone holds it, which happens with a job change that sets their position_id
(POST /people/{person_id}/job-changes). Positions are part of Growth, Pro and Enterprise, so in test mode an
organisation on another plan gets plan_required.
- Fires
position.created.
Scope: hr:write · Plan: Pro and Enterprise in live mode; Growth, Pro and Enterprise in test mode.
AuthorizationBearer <token>Send Authorization: Bearer <token> on every request. The token is one of:
| Prefix | What it is | Where it may be used |
|---|---|---|
agoo_sk_live_ | Secret key, live mode | Your servers only |
agoo_sk_test_ | Secret key, test mode | Your servers only |
agoo_pk_live_ | Publishable key, live mode | Browsers and apps: create pre-registrations and bookings, read public booking types (with their intake questions) and their free slots, read the visit types open for pre-registration with their public forms. Never lists people. |
agoo_pk_test_ | Publishable key, test mode | As above, in test mode |
Admins create keys in Console → Developers → API keys and choose each key's scopes. A key is shown once. Never put a secret key in a URL, a browser or a mobile app.
Scope: hr:write
Idempotency-Key*stringA UUID you generate for this operation. If you retry with the same key within 24 hours, Agoo returns the
original response instead of acting twice. While the first request is still running, a retry returns
conflict. Reusing a key with a different request returns idempotency_key_reused. Responses with
rate_limited, internal_error or unavailable aren't stored, so retry those with the same key.
uuidapplication/json- body
A new position. It starts vacant unless you freeze or close it.
title*string1 <= length <= 120department_id?DepartmentIdOne of your departments that isn't archived.
^dept_[0-7][0-9a-hjkmnp-tv-z]{25}$"dept_01kjsd2v80e5q7m3c9x4ht6wfa"site_id?SiteIdA site ID (TypeID with the site prefix).
^site_[0-7][0-9a-hjkmnp-tv-z]{25}$"site_01kjpt3yw0fz0v414608h9x65s"reports_to_id?PositionIdA position that isn't closed, never one that reports to this one.
^pos_[0-7][0-9a-hjkmnp-tv-z]{25}$"pos_01m6h2k4q8e7r9t1v3w5x7y9za"grade?stringlength <= 60fte?numberFull-time equivalent.
0.1 <= value <= 11status?string"vacant""vacant""frozen""closed"budgeted?booleantrueThe new position.
application/json- response
A seat in your organisation, with its department, site, grade, the position it reports to, and who holds it. Positions are part of Growth, Pro and Enterprise.
id*PositionIdA position ID (TypeID with the pos prefix).
^pos_[0-7][0-9a-hjkmnp-tv-z]{25}$"pos_01m6h2k4q8e7r9t1v3w5x7y9za"object*"position""position"title*string1 <= length <= 120"Senior operations officer"department_id*|nullsite_id*|nullreports_to_id*|The position it reports to. null: none.
grade*|length <= 60"G6"fte*numberFull-time equivalent, from 0.1 to 1.
0.1 <= value <= 11status*PositionStatusfilled: someone holds it now. vacant: nobody does, and it may be filled. frozen: nobody is to be hired
for it now. closed: it no longer exists, and is kept for its history. filled and vacant follow who holds
it; frozen and closed are chosen.
"filled""vacant""frozen""closed"budgeted*booleanWhether it's in the budget.
holder_id*|The person who holds it now. null: nobody.
held_since*|Since when the holder has held it.
datecreated_at*stringdate-timeupdated_at*stringdate-timecurl -X POST 'https://api.agoohq.com/v1/positions' \ -H 'Idempotency-Key: 3f6b2a1e-8c4d-4f7a-9b2e-5d1c0a7e9f64' \ -H 'Content-Type: application/json' \ -d '{ "title": "Senior operations officer", "department_id": "dept_01kjsd2v80e5q7m3c9x4ht6wfb", "site_id": "site_01kjpt3yw0fz0v414608h9x65s", "reports_to_id": "pos_01m6h1a3c5e7g9j1k3m5n7p9qr", "grade": "G6"}'{ "id": "pos_01m6h2k4q8e7r9t1v3w5x7y9za", "object": "position", "title": "Senior operations officer", "department_id": "dept_01kjsd2v80e5q7m3c9x4ht6wfb", "site_id": "site_01kjpt3yw0fz0v414608h9x65s", "reports_to_id": "pos_01m6h1a3c5e7g9j1k3m5n7p9qr", "grade": "G6", "fte": 1, "status": "vacant", "budgeted": true, "holder_id": null, "held_since": null, "created_at": "2026-10-12T08:40:00Z", "updated_at": "2026-10-12T08:40:00Z"}List positions GET
Returns your organisation's positions by title, each with who holds it now. Filter by department, or by status: `vacant` lists your vacancies. **Scope:** `people:read` · **Plan:** Pro and Enterprise in live mode; every plan in test mode.
Get a position GET
Returns one position, with who holds it now and since when. **Scope:** `people:read` · **Plan:** Pro and Enterprise in live mode; every plan in test mode.