Docs
API referencePeople and H R

Create a position

Preview· P9
POST
/positions

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.

Authorization

headerAuthorizationBearer <token>

Send Authorization: Bearer <token> on every request. The token is one of:

PrefixWhat it isWhere it may be used
agoo_sk_live_Secret key, live modeYour servers only
agoo_sk_test_Secret key, test modeYour servers only
agoo_pk_live_Publishable key, live modeBrowsers 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 modeAs 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

Header Parameters

Idempotency-Key*string

A 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.

Formatuuid

Request Body

application/json
  1. body

A new position. It starts vacant unless you freeze or close it.

title*string
Length1 <= length <= 120
department_id?DepartmentId

One of your departments that isn't archived.

Match^dept_[0-7][0-9a-hjkmnp-tv-z]{25}$
Example"dept_01kjsd2v80e5q7m3c9x4ht6wfa"
site_id?SiteId

A site ID (TypeID with the site prefix).

Match^site_[0-7][0-9a-hjkmnp-tv-z]{25}$
Example"site_01kjpt3yw0fz0v414608h9x65s"
reports_to_id?PositionId

A position that isn't closed, never one that reports to this one.

Match^pos_[0-7][0-9a-hjkmnp-tv-z]{25}$
Example"pos_01m6h2k4q8e7r9t1v3w5x7y9za"
grade?string
Lengthlength <= 60
fte?number

Full-time equivalent.

Range0.1 <= value <= 1
Default1
status?string
Default"vacant"
Value in"vacant""frozen""closed"
budgeted?boolean
Defaulttrue

Response Body

The new position.

application/json
  1. 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*PositionId

A position ID (TypeID with the pos prefix).

Match^pos_[0-7][0-9a-hjkmnp-tv-z]{25}$
Example"pos_01m6h2k4q8e7r9t1v3w5x7y9za"
object*"position"
Value in"position"
title*string
Length1 <= length <= 120
Example"Senior operations officer"
department_id*|null
site_id*|null
reports_to_id*|

The position it reports to. null: none.

grade*|
Lengthlength <= 60
Example"G6"
fte*number

Full-time equivalent, from 0.1 to 1.

Range0.1 <= value <= 1
Example1
status*PositionStatus

filled: 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.

Value in"filled""vacant""frozen""closed"
budgeted*boolean

Whether it's in the budget.

holder_id*|

The person who holds it now. null: nobody.

held_since*|

Since when the holder has held it.

Formatdate
created_at*string
Formatdate-time
updated_at*string
Formatdate-time
curl -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"}