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