# Problems

Every error document /api/v2 returns. All of them are `application/problem+json` (RFC 9457).

## Every problem carries
- type (url, required): The problem type. This is the only member a client switches on.
- title (string, required): Short, stable per type, English, never interpolated. Never parse it.
- status (integer, required): The HTTP status, repeated in the body.
- detail (string, required): Human-readable explanation. May quote values from your request; never shows internal error output.
- instance (string, required): The request path, with no query string.
- request_id (string, required): The id also in the X-Request-Id header. Quote it when you report a problem to plnnrly support.

## Which problem a status can be
- 400: idempotency-key-required | invalid-cursor | invalid-query-parameter | malformed-request | unsupported-parameter. Bad request.
- 401: token-expired | unauthenticated. Unauthenticated.
- 403: forbidden-by-policy | insufficient-permission | insufficient-token-scope. Forbidden.
- 404: endpoint-not-found | resource-not-found. Not found.
- 405: method-not-allowed. Method not allowed.
- 406: not-acceptable. Not acceptable.
- 409Idempotency: idempotency-key-in-flight | idempotency-key-reused. Conflict.
- 412: precondition-failed. Precondition failed.
- 415: unsupported-media-type. Unsupported media type.
- 422: unprocessable-relationship | validation-failed. Unprocessable content.
- 429: rate-limit-exceeded. Too many requests.
- 500: internal-error. Server error.
- 503: tenant-unavailable. Service unavailable.

## The documents

### DuplicateResourceProblem

- Status: 409
- Type: https://developers.plnnrly.com/api/v2/problems/duplicate-resource
- Title: Duplicate resource

Extra members:
- conflict (object, optional): Machine-readable context for the uniqueness failure.
  - field (string, required): The field whose value is already taken.
  - value (string, required): The rejected value.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/duplicate-resource",
  "title": "Duplicate resource",
  "status": 409,
  "detail": "Another project already uses the code \"ACM\".",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "conflict": {
    "field": "code",
    "value": "ACM"
  }
}
```

### EndpointNotFoundProblem

- Status: 404
- Type: https://developers.plnnrly.com/api/v2/problems/endpoint-not-found
- Title: Endpoint not found

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/endpoint-not-found",
  "title": "Endpoint not found",
  "status": 404,
  "detail": "No API endpoint matches this request.",
  "instance": "/api/v2/nope",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### EndpointRetiredProblem

- Status: 410
- Type: https://developers.plnnrly.com/api/v2/problems/endpoint-retired
- Title: Endpoint retired

Extra members:
- sunset (date (YYYY-MM-DD), optional): The date this endpoint stopped working.
- replaced_by (string or null, optional): The route name that replaces it.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/endpoint-retired",
  "title": "Endpoint retired",
  "status": 410,
  "detail": "This endpoint has been retired and no longer serves requests.",
  "instance": "/api/v2/projects/4711/statistics",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "sunset": "2027-09-01",
  "replaced_by": "api.v2.projects.show"
}
```

### ForbiddenByPolicyProblem

- Status: 403
- Type: https://developers.plnnrly.com/api/v2/problems/forbidden-by-policy
- Title: Forbidden by policy

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/forbidden-by-policy",
  "title": "Forbidden by policy",
  "status": 403,
  "detail": "You may not perform this operation on this record.",
  "instance": "/api/v2/projects/4711",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### IdempotencyKeyInFlightProblem

- Status: 409
- Type: https://developers.plnnrly.com/api/v2/problems/idempotency-key-in-flight
- Title: Idempotency key in flight

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/idempotency-key-in-flight",
  "title": "Idempotency key in flight",
  "status": 409,
  "detail": "The original request for this Idempotency-Key is still being processed. Retry shortly.",
  "instance": "/api/v2/worklogs",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### IdempotencyKeyRequiredProblem

- Status: 400
- Type: https://developers.plnnrly.com/api/v2/problems/idempotency-key-required
- Title: Idempotency key required

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/idempotency-key-required",
  "title": "Idempotency key required",
  "status": 400,
  "detail": "This endpoint requires an Idempotency-Key header.",
  "instance": "/api/v2/worklogs",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### IdempotencyKeyReusedProblem

- Status: 409
- Type: https://developers.plnnrly.com/api/v2/problems/idempotency-key-reused
- Title: Idempotency key reused

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/idempotency-key-reused",
  "title": "Idempotency key reused",
  "status": 409,
  "detail": "This Idempotency-Key was already used with a different request body.",
  "instance": "/api/v2/worklogs",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### InsufficientPermissionProblem

- Status: 403
- Type: https://developers.plnnrly.com/api/v2/problems/insufficient-permission
- Title: Insufficient permission

Extra members:
- required_permission (string, optional): The permission the user is missing, e.g. `projects::read`.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/insufficient-permission",
  "title": "Insufficient permission",
  "status": 403,
  "detail": "Your role does not carry the permission this endpoint requires.",
  "instance": "/api/v2/projects/4711",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "required_permission": "projects::read"
}
```

### InsufficientTokenScopeProblem

- Status: 403
- Type: https://developers.plnnrly.com/api/v2/problems/insufficient-token-scope
- Title: Insufficient token scope

Extra members:
- required_ability (array of string, optional): The token abilities, any one of which would have permitted this request, e.g. `["api:read:project_drive", "api:read:customer_drive"]`. A `*`-prefix route lists every ability of the required class.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/insufficient-token-scope",
  "title": "Insufficient token scope",
  "status": 403,
  "detail": "This token does not carry an ability that covers this endpoint.",
  "instance": "/api/v2/drive-documents/91",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "required_ability": [
    "api:read:project_drive",
    "api:read:customer_drive"
  ]
}
```

### InternalErrorProblem

- Status: 500
- Type: https://developers.plnnrly.com/api/v2/problems/internal-error
- Title: Internal error

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/internal-error",
  "title": "Internal error",
  "status": 500,
  "detail": "An unexpected error occurred. Quote the request_id when contacting support.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### InvalidCursorProblem

- Status: 400
- Type: https://developers.plnnrly.com/api/v2/problems/invalid-cursor
- Title: Invalid cursor

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/invalid-cursor",
  "title": "Invalid cursor",
  "status": 400,
  "detail": "The cursor is not valid for this query. Restart the scan without a cursor.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### InvalidQueryParameterProblem

- Status: 400
- Type: https://developers.plnnrly.com/api/v2/problems/invalid-query-parameter
- Title: Invalid query parameter

Extra members:
- errors (array of object, optional): One entry per fault. Each carries exactly one of `pointer` or `parameter`.
  - code (string, required): Machine-readable fault code. Switch on this, never on detail.
  - detail (string, required): Human-readable explanation; enumerates the allowed set for an allow-list failure.
  - pointer (string or null, optional): RFC 6901 pointer into the request body, e.g. `/minutes`.
  - parameter (string or null, optional): The query-parameter name, brackets included, e.g. `filter[start_date][gte]`.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/invalid-query-parameter",
  "title": "Invalid query parameter",
  "status": 400,
  "detail": "1 query parameter was not recognised or not permitted on this resource.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "errors": [
    {
      "parameter": "limit",
      "code": "unknown_parameter",
      "detail": "Unknown query parameter \"limit\". Allowed: filter[…], sort, include, q, trashed, per_page, cursor, page, with_total, view, meta, force, url_ttl_minutes."
    }
  ]
}
```

### MalformedRequestProblem

- Status: 400
- Type: https://developers.plnnrly.com/api/v2/problems/malformed-request
- Title: Malformed request

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/malformed-request",
  "title": "Malformed request",
  "status": 400,
  "detail": "The request could not be read.",
  "instance": "/api/v2/worklogs",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### MethodNotAllowedProblem

- Status: 405
- Type: https://developers.plnnrly.com/api/v2/problems/method-not-allowed
- Title: Method not allowed

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/method-not-allowed",
  "title": "Method not allowed",
  "status": 405,
  "detail": "PUT is not supported. This API patches: use PATCH with only the fields you are changing.",
  "instance": "/api/v2/projects/4711",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### NotAcceptableProblem

- Status: 406
- Type: https://developers.plnnrly.com/api/v2/problems/not-acceptable
- Title: Not acceptable

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/not-acceptable",
  "title": "Not acceptable",
  "status": 406,
  "detail": "This API only produces application/json. Send an Accept header that allows it.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### PreconditionFailedProblem

- Status: 412
- Type: https://developers.plnnrly.com/api/v2/problems/precondition-failed
- Title: Precondition failed

Extra members:
- current_version (integer or null, optional): The record's current version, where it has one. Null otherwise.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/precondition-failed",
  "title": "Precondition failed",
  "status": 412,
  "detail": "The record changed since the version named in If-Match. Re-read it and retry.",
  "instance": "/api/v2/work-items/812",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "current_version": 12
}
```

### RateLimitExceededProblem

- Status: 429
- Type: https://developers.plnnrly.com/api/v2/problems/rate-limit-exceeded
- Title: Rate limit exceeded

Extra members:
- rate_limit (object, optional): Which ceiling was exhausted, and for how long.
  - limit (integer, required): Units permitted per minute in the exhausted bucket.
  - retry_after (integer, required): Seconds until the bucket refills.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/rate-limit-exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "The rate limit for this caller has been exhausted. Retry after the interval in the Retry-After header.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "rate_limit": {
    "limit": 600,
    "retry_after": 42
  }
}
```

### ResourceNotFoundProblem

- Status: 404
- Type: https://developers.plnnrly.com/api/v2/problems/resource-not-found
- Title: Resource not found

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/resource-not-found",
  "title": "Resource not found",
  "status": 404,
  "detail": "No Project was found with id 4711.",
  "instance": "/api/v2/projects/4711",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### StateConflictProblem

- Status: 409
- Type: https://developers.plnnrly.com/api/v2/problems/state-conflict
- Title: State conflict

Extra members:
- conflict (object, optional): Machine-readable context. `reason` is always present; the other keys depend on it.
  - reason (string, required): Why the record's current state forbids the operation.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/state-conflict",
  "title": "State conflict",
  "status": 409,
  "detail": "The target status is not reachable from this work item's current status.",
  "instance": "/api/v2/work-items/812/actions/transition",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "conflict": {
    "reason": "illegal_transition",
    "from_workflow_status_id": 3,
    "to_workflow_status_id": 9
  }
}
```

### TenantUnavailableProblem

- Status: 503
- Type: https://developers.plnnrly.com/api/v2/problems/tenant-unavailable
- Title: Tenant unavailable

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/tenant-unavailable",
  "title": "Tenant unavailable",
  "status": 503,
  "detail": "This organisation is provisioning, suspended or archived, or the service is down for maintenance.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### TokenExpiredProblem

- Status: 401
- Type: https://developers.plnnrly.com/api/v2/problems/token-expired
- Title: Token expired

Extra members:
- expired_at (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): When the token expired.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/token-expired",
  "title": "Token expired",
  "status": 401,
  "detail": "This token has expired. Create a new one under API access in your user menu.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "expired_at": "2026-08-01T09:00:00Z"
}
```

### UnauthenticatedProblem

- Status: 401
- Type: https://developers.plnnrly.com/api/v2/problems/unauthenticated
- Title: Unauthenticated

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/unauthenticated",
  "title": "Unauthenticated",
  "status": 401,
  "detail": "No valid bearer token was supplied.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### UnprocessableRelationshipProblem

- Status: 422
- Type: https://developers.plnnrly.com/api/v2/problems/unprocessable-relationship
- Title: Unprocessable relationship

Extra members:
- errors (array of object, optional): One entry per fault. Each carries exactly one of `pointer` or `parameter`.
  - code (string, required): Machine-readable fault code. Switch on this, never on detail.
  - detail (string, required): Human-readable explanation; enumerates the allowed set for an allow-list failure.
  - pointer (string or null, optional): RFC 6901 pointer into the request body, e.g. `/minutes`.
  - parameter (string or null, optional): The query-parameter name, brackets included, e.g. `filter[start_date][gte]`.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/unprocessable-relationship",
  "title": "Unprocessable relationship",
  "status": 422,
  "detail": "1 referenced record cannot be used here.",
  "instance": "/api/v2/work-items",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "errors": [
    {
      "pointer": "/sprint_id",
      "code": "exists",
      "detail": "Sprint 55 belongs to another project and cannot be used here."
    }
  ]
}
```

### UnsupportedMediaTypeProblem

- Status: 415
- Type: https://developers.plnnrly.com/api/v2/problems/unsupported-media-type
- Title: Unsupported media type

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/unsupported-media-type",
  "title": "Unsupported media type",
  "status": 415,
  "detail": "This API only accepts application/json request bodies.",
  "instance": "/api/v2/worklogs",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S"
}
```

### UnsupportedParameterProblem

- Status: 400
- Type: https://developers.plnnrly.com/api/v2/problems/unsupported-parameter
- Title: Unsupported parameter

Extra members:
- errors (array of object, optional): One entry per fault. Each carries exactly one of `pointer` or `parameter`.
  - code (string, required): Machine-readable fault code. Switch on this, never on detail.
  - detail (string, required): Human-readable explanation; enumerates the allowed set for an allow-list failure.
  - pointer (string or null, optional): RFC 6901 pointer into the request body, e.g. `/minutes`.
  - parameter (string or null, optional): The query-parameter name, brackets included, e.g. `filter[start_date][gte]`.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/unsupported-parameter",
  "title": "Unsupported parameter",
  "status": 400,
  "detail": "1 query parameter is not supported on this endpoint.",
  "instance": "/api/v2/projects",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "errors": [
    {
      "parameter": "fields",
      "code": "unsupported_parameter",
      "detail": "Sparse fieldsets are not supported by this API. Use view=summary or view=detail instead."
    }
  ]
}
```

### ValidationFailedProblem

- Status: 422
- Type: https://developers.plnnrly.com/api/v2/problems/validation-failed
- Title: Validation failed

Extra members:
- errors (array of object, optional): One entry per fault. Each carries exactly one of `pointer` or `parameter`.
  - code (string, required): Machine-readable fault code. Switch on this, never on detail.
  - detail (string, required): Human-readable explanation; enumerates the allowed set for an allow-list failure.
  - pointer (string or null, optional): RFC 6901 pointer into the request body, e.g. `/minutes`.
  - parameter (string or null, optional): The query-parameter name, brackets included, e.g. `filter[start_date][gte]`.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "3 fields failed validation.",
  "instance": "/api/v2/worklogs",
  "request_id": "01K4B7Q9YV3PZ2N8T1E6M0XR4S",
  "errors": [
    {
      "pointer": "/date",
      "code": "date_format",
      "detail": "The date does not match the format Y-m-d."
    },
    {
      "pointer": "/minutes",
      "code": "min",
      "detail": "The minutes must be at least 1."
    },
    {
      "pointer": "/description",
      "code": "required",
      "detail": "The description field is required."
    }
  ]
}
```
