Conventions
Errors
Every non-2xx response from the Properly Platform API is an RFC 7807 problem document. The type URL identifies the problem and links to its page here.
The problem document
Every non-2xx response has Content-Type: application/problem+json and this shape:
{
"type": "https://www.getproperly.com/developers/errors/validation",
"title": "Validation failed",
"status": 400,
"detail": "One or more fields are invalid",
"instance": "/v1/properties",
"code": "validation_failed",
"requestId": "aQ3fT8xW1m",
"errors": [
{
"field": "name",
"code": "stringMin",
"message": "The 'name' field length must be greater than or equal to 3 characters long."
},
{
"field": "unexpectedField",
"code": "objectStrict",
"message": "The object '' contains forbidden keys: 'unexpectedField'."
}
]
} Problem types
| Status | type | code | title | When |
|---|---|---|---|---|
400 | …/errors/validation | validation_failed | Validation failed | The request shape is invalid (the response carries an errors[] array naming each field), or a domain-level rule rejected the request (detail only): for example a sourcePropertyId that does not resolve, an image upload that failed, or an invalid dateFrom. |
401 | …/errors/unauthorized | unauthorized | Unauthorized | The x-api-key header is missing or does not match an active key. |
403 | …/errors/subscription-inactive | subscription_inactive | Subscription inactive | The key is valid, but the account's Properly subscription has expired or its plan doesn't include the API. Webhooks aren't delivered while this lasts. |
404 | …/errors/not-found | not_found | Not found | The resource does not exist, or it belongs to a different account than the one the API key is scoped to. |
409 | …/errors/conflict | conflict | Conflict | The request collides with existing state: for example a duplicate sourceBookingId for the account, or a webhook URL that already has a subscription. |
422 | …/errors/idempotency-key-reuse | idempotency_key_reuse | Idempotency key reuse | The Idempotency-Key header was already used within its ~24 hour window with a different request body. |
429 | …/errors/rate-limited | rate_limited | Rate limit exceeded | The API key exceeded the rolling 2-second window (100 requests) or the rolling 24-hour quota (30,000 requests). |
500 | …/errors/internal | internal_error | Internal server error | An unexpected error occurred while processing the request. Internals are never exposed; detail is always "Unexpected error". |
The full type value is
https://www.getproperly.com/developers/errors/<slug>. Both type
and code are a frozen contract you may compare as strings. Branch on
code or status, never on detail text.
Facts
- Strict validation: unknown request-body fields are rejected, not ignored.
Each offending key is named in
errors[]withcode: "objectStrict". -
errors[]appears on shape-validation 400s. Each entry is{ field, code, message }, wherecodeis a machine-readable validator code:required,stringMin,stringMax,number,enumValue,url,email,objectStrict, and others. -
instanceis the request path. -
codeis a stable machine-readable identifier for the error class, one per status:validation_failed,unauthorized,subscription_inactive,not_found,conflict,idempotency_key_reuse,rate_limited,internal_error. Liketype, it is a frozen contract you may compare as a string. -
requestIdechoes thex-request-idresponse header. Quote it in support requests. -
A webhook-URL 409 additionally carries
existingId, the id of the subscription that already uses the URL.
Success codes
| Status | Meaning |
|---|---|
200 | Read or update succeeded; the body is the resource or result. |
201 | Created; the body is the created resource with its server-assigned id. |
202 | Accepted; used by the webhook test-fire, which delivers asynchronously. |
204 | Deleted; no body. |