Update a position
Changes a position's title, department, site, grade, the position it reports to, its full-time equivalent or
whether it's budgeted, or freezes, closes or reopens it. Send only the fields you want to change; null clears
an optional one.
- A position someone holds can't be frozen or closed (
invalid_state): change their job first. status: vacantreopens a frozen or closed position; it showsfilledwhile someone holds it. Who holds a position is changed by a job change, never here.- Fires
position.updated, withprevious_attributes.changednaming what changed.
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
position_id*stringThe position's ID.
^pos_[0-7][0-9a-hjkmnp-tv-z]{25}$"pos_01m6h2k4q8e7r9t1v3w5x7y9za"application/json- body
The fields to change. Send null to clear an optional field.
title?string1 <= length <= 120department_id?|nullsite_id?|nullreports_to_id?|nullgrade?|length <= 60fte?number0.1 <= value <= 1status?stringvacant reopens it (it shows filled while someone holds it). One somebody holds can't be frozen or closed.
"vacant""frozen""closed"budgeted?booleanThe updated 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 PATCH 'https://api.agoohq.com/v1/positions/pos_01m6h2k4q8e7r9t1v3w5x7y9za' \ -H 'Content-Type: application/json' \ -d '{ "status": "frozen"}'{ "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": "frozen", "budgeted": true, "holder_id": null, "held_since": null, "created_at": "2026-10-12T08:40:00Z", "updated_at": "2026-10-19T10:20:00Z"}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.
Get a person's HR profile GET
Returns the person's personal and employment details, their pension schemes and their statutory numbers by kind and last four only. Full statutory numbers, pay accounts and emergency contacts, next of kin and beneficiaries are never in the API. Someone with no HR details recorded yet comes back with `null`s and an `open_ended` contract. **Scope:** `hr:read` · **Plan:** Pro and Enterprise in live mode; every plan in test mode.