Credentially Placements API
Placement lifecycle proxy endpoints — assignment of an employee to a location + role within the organisation.
Placement lifecycle proxy endpoints — assignment of an employee to a location + role within the organisation.
openapi: 3.1.0
info:
title: Credentially Public Compliance-packages Placements API
description: Public API Proxy with Rate Limiting and Audit
version: 2.0.0
servers:
- url: https://app.credentially.io/gateway
description: Generated server url
tags:
- name: Placements
description: Placement lifecycle proxy endpoints — assignment of an employee to a location + role within the organisation.
paths:
/api/placements/{id}/stage:
put:
tags:
- Placements
summary: Assign a placement to a stage
description: 'Idempotent upsert of the placement''s board position. Optional position; 409 on stage disabled or version conflict.
**Rate Limit:** `remove-placement-stage` (25 req / 1s)'
operationId: assignStage
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/AssignStageRequest'
required: true
responses:
'200':
description: Stage assignment created or updated.
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementResponseDto'
'404':
description: Placement or stage not found in this organisation
'409':
description: Target stage disabled or version conflict
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
delete:
tags:
- Placements
summary: Remove a placement from the stages board
description: 'Idempotent — 204 whether or not the placement was on the board.
**Rate Limit:** `remove-placement-stage` (25 req / 1s)'
operationId: removeFromBoard
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'204':
description: Placement removed from the board (or was not on it)
'403':
description: Token lacks scope, or placement not in caller's organisation
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
/api/placements/{id}:
get:
tags:
- Placements
summary: Get a placement by public id
description: '
**Rate Limit:** `delete-placement` (25 req / 1s)'
operationId: get
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'200':
description: Placement found
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementResponseDto'
'401':
description: Missing or invalid bearer token
'403':
description: Token lacks scope, or placement not in caller's organisation
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
put:
tags:
- Placements
summary: Update a placement's mutable fields
description: '
**Rate Limit:** `delete-placement` (25 req / 1s)'
operationId: update_1
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UpdatePlacementRequest'
required: true
responses:
'200':
description: Placement updated
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementResponseDto'
'400':
description: Validation failed
'403':
description: Token lacks scope, or placement not in caller's organisation
'409':
description: approvalDueDate cannot be changed once approved/started
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
delete:
tags:
- Placements
summary: Hard-delete a placement
description: 'Allowed only when PENDING and the start date has not passed; otherwise 409 — use cancel instead.
**Rate Limit:** `delete-placement` (25 req / 1s)'
operationId: delete
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'204':
description: Placement deleted
'409':
description: Placement not PENDING, or start date elapsed
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
/api/placements/{id}/stage/prev:
post:
tags:
- Placements
summary: Move placement to the previous enabled stage
description: '
**Rate Limit:** `move-placement-stage` (25 req / 1s)'
operationId: moveStagePrev
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'200':
description: Moved to the previous stage.
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementResponseDto'
'404':
description: Placement not found
'409':
description: Already at the first stage
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
/api/placements/{id}/stage/next:
post:
tags:
- Placements
summary: Move placement to the next enabled stage
description: '
**Rate Limit:** `move-placement-stage` (25 req / 1s)'
operationId: moveStageNext
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'200':
description: Moved to the next stage.
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementResponseDto'
'404':
description: Placement not found
'409':
description: Already at the last stage
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
/api/placements/{id}/cancel:
post:
tags:
- Placements
summary: Cancel (soft-delete) a placement
description: 'Sets status to CANCELLED with the supplied reason. Allowed from PENDING or ACTIVE; 409 from COMPLETED.
**Rate Limit:** `cancel-placement` (25 req / 1s)'
operationId: cancel
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CancelPlacementRequest'
required: true
responses:
'204':
description: Placement cancelled
'400':
description: reason missing or blank
'409':
description: Placement is COMPLETED — cannot be cancelled
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
/api/placements/{id}/approve:
post:
tags:
- Placements
summary: Approve a placement
description: 'Records the calling user as approver + timestamp. Allowed only while PENDING and not already approved.
**Rate Limit:** `approve-placement` (25 req / 1s)'
operationId: approve
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'200':
description: Placement approved
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementResponseDto'
'403':
description: Token lacks scope, or placement not in caller's organisation
'409':
description: Placement not PENDING or already approved
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
/api/placements:
get:
tags:
- Placements
summary: List placements (paginated, filterable)
description: 'Unified filter contract — see the per-parameter docs. Default sort is startDate DESC; override with ?sort=field,dir.
**Rate Limit:** `list-placements` (100 req / 1s)'
operationId: list
parameters:
- name: employeePublicId
in: query
description: Filter by employee public id (singular). Mutually exclusive with employeePublicIds.
required: false
schema:
type: string
format: uuid
description: Filter by employee public id (singular). Mutually exclusive with employeePublicIds.
- name: employeePublicIds
in: query
description: Filter by employee public ids (plural, max 50). Mutually exclusive with employeePublicId.
required: false
schema:
type: array
description: Filter by employee public ids (plural, max 50). Mutually exclusive with employeePublicId.
items:
type: string
format: uuid
description: Filter by employee public ids (plural, max 50). Mutually exclusive with employeePublicId.
maxItems: 50
minItems: 0
- name: locationPublicIds
in: query
description: Filter by location public ids (plural, max 50)
required: false
schema:
type: array
description: Filter by location public ids (plural, max 50)
items:
type: string
format: uuid
description: Filter by location public ids (plural, max 50)
maxItems: 50
minItems: 0
- name: jurisdictionPublicIds
in: query
description: Filter by jurisdiction public ids (plural, max 50)
required: false
schema:
type: array
description: Filter by jurisdiction public ids (plural, max 50)
items:
type: string
format: uuid
description: Filter by jurisdiction public ids (plural, max 50)
maxItems: 50
minItems: 0
- name: rolePublicIds
in: query
description: Filter by role public ids (plural, max 50)
required: false
schema:
type: array
description: Filter by role public ids (plural, max 50)
items:
type: string
format: uuid
description: Filter by role public ids (plural, max 50)
maxItems: 50
minItems: 0
- name: status
in: query
description: Filter by lifecycle status
required: false
schema:
type: string
description: Filter by lifecycle status
enum:
- PENDING
- ACTIVE
- COMPLETED
- CANCELLED
- PENDING
- ACTIVE
- COMPLETED
- name: complianceStatus
in: query
description: Filter by compliance status
required: false
schema:
type: string
description: Filter by compliance status
enum:
- COMPLIANT
- NOT_COMPLIANT
- COMPLIANT
- NOT_COMPLIANT
- name: startDateFrom
in: query
description: Absolute start date floor (inclusive). Mutually exclusive with startsWithinDays.
required: false
schema:
type: string
format: date
description: Absolute start date floor (inclusive). Mutually exclusive with startsWithinDays.
- name: startDateTo
in: query
description: Absolute start date ceiling (inclusive). Mutually exclusive with startsWithinDays.
required: false
schema:
type: string
format: date
description: Absolute start date ceiling (inclusive). Mutually exclusive with startsWithinDays.
- name: startsWithinDays
in: query
description: Relative start window — N days from today (inclusive). Mutually exclusive with startDateFrom/To.
required: false
schema:
type: integer
format: int32
description: Relative start window — N days from today (inclusive). Mutually exclusive with startDateFrom/To.
- name: page
in: query
required: false
schema:
type: integer
format: int32
default: 0
- name: size
in: query
required: false
schema:
type: integer
format: int32
default: 20
- name: sort
in: query
required: false
schema:
type: array
items:
type: string
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'200':
description: Page of placements
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementPageDto'
'400':
description: Filter validation failure
'401':
description: Missing or invalid bearer token
'403':
description: Token lacks required scope
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
post:
tags:
- Placements
summary: Create a placement
description: 'Created in PENDING status. jobPositionId is optional (auto-resolved when omitted).
**Rate Limit:** `list-placements` (100 req / 1s)'
operationId: create
parameters:
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePlacementRequest'
required: true
responses:
'201':
description: Placement created
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementResponseDto'
'400':
description: Validation failed
'401':
description: Missing or invalid bearer token
'403':
description: Token lacks required scope
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
/api/placements/notes:
get:
tags:
- Placements
summary: List all placement notes for the organisation (paginated)
description: '
**Rate Limit:** `list-placement-notes-org` (100 req / 1s)'
operationId: listAllNotes
parameters:
- name: page
in: query
required: false
schema:
type: integer
format: int32
default: 0
- name: size
in: query
required: false
schema:
type: integer
format: int32
default: 20
- name: sort
in: query
required: false
schema:
type: array
items:
type: string
- name: X-API-Version
in: header
schema:
type: string
default: 2.0.0
enum:
- 2.0.0
responses:
'200':
description: Page of notes
content:
'*/*':
schema:
$ref: '#/components/schemas/PlacementNotePageDto'
'401':
description: Missing or invalid bearer token
'403':
description: Token lacks organisation.placement.view scope
'429':
description: Too many requests - the rate limit for this endpoint has been exceeded. Use the Retry-After header to determine when to retry.
headers:
Retry-After:
description: Number of seconds to wait before retrying the request.
schema:
type: integer
format: int32
example: 1
security:
- bearer-key: []
components:
schemas:
PlacementResponseDto:
type: object
properties:
publicId:
type: string
format: uuid
employeePublicId:
type: string
format: uuid
jobPositionId:
type: integer
format: int64
rolePublicId:
type: string
format: uuid
locationPublicId:
type: string
format: uuid
jurisdictionPublicId:
type: string
format: uuid
status:
type: string
enum:
- PENDING
- ACTIVE
- COMPLETED
- CANCELLED
complianceStatus:
type: string
enum:
- COMPLIANT
- NOT_COMPLIANT
compliancePercentage:
type: integer
format: int32
complianceEvaluationSeq:
type: integer
format: int64
complianceEvaluatedAsOf:
type: string
format: date
startDate:
type: string
format: date
endDate:
type: string
format: date
approvalDueDate:
type: string
format: date
approvedBy:
type: string
format: uuid
approvedAt:
type: string
format: date-time
stagePublicId:
type: string
format: uuid
notesCount:
type: integer
format: int32
latestNotes:
type: array
items:
$ref: '#/components/schemas/PlacementNotePreviewDto'
createdAt:
type: string
format: date-time
createdBy:
type: string
format: uuid
updatedAt:
type: string
format: date-time
updatedBy:
type: string
format: uuid
UpdatePlacementRequest:
type: object
properties:
rolePublicId:
type: string
format: uuid
locationPublicId:
type: string
format: uuid
startDate:
type: string
format: date
endDate:
type: string
format: date
approvalDueDate:
type: string
format: date
required:
- locationPublicId
- rolePublicId
- startDate
PlacementNotePreviewDto:
type: object
properties:
content:
type: string
createdAt:
type: string
format: date-time
CreatePlacementRequest:
type: object
properties:
employeePublicId:
type: string
format: uuid
jobPositionId:
type: integer
format: int64
rolePublicId:
type: string
format: uuid
locationPublicId:
type: string
format: uuid
startDate:
type: string
format: date
endDate:
type: string
format: date
approvalDueDate:
type: string
format: date
compliancePackageIds:
type: array
items:
type: string
format: uuid
maxItems: 50
minItems: 0
packageRequirementOverrides:
type: object
additionalProperties:
type: object
additionalProperties: {}
required:
- employeePublicId
- locationPublicId
- rolePublicId
- startDate
PlacementNotePageDto:
type: object
properties:
content:
type: array
items:
$ref: '#/components/schemas/PlacementNoteResponseDto'
totalPages:
type: integer
format: int32
totalElements:
type: integer
format: int64
size:
type: integer
format: int32
number:
type: integer
format: int32
PlacementPageDto:
type: object
properties:
content:
type: array
items:
$ref: '#/components/schemas/PlacementResponseDto'
totalPages:
type: integer
format: int32
totalElements:
type: integer
format: int64
size:
type: integer
format: int32
number:
type: integer
format: int32
AssignStageRequest:
type: object
properties:
stagePublicId:
type: string
format: uuid
position:
type: integer
format: int64
required:
- stagePublicId
CancelPlacementRequest:
type: object
properties:
reason:
type: string
maxLength: 2000
minLength: 0
required:
- reason
PlacementNoteResponseDto:
type: object
properties:
publicId:
type: string
format: uuid
placementPublicId:
type: string
format: uuid
employeePublicId:
type: string
format: uuid
locationName:
type: string
roleName:
type: string
content:
type: string
createdAt:
type: string
format: date-time
createdBy:
type: string
format: uuid
createdByName:
type: string
createdByJobRole:
type: string
updatedAt:
type: string
format: date-time
updatedBy:
type: string
format: uuid
securitySchemes:
bearer-key:
type: http
scheme: bearer
bearerFormat: JWT