Migration
Migrating from the legacy API
The legacy Properly API was a single batched endpoint that acknowledged immediately and delivered every result, including validation errors, later via webhook. The Platform API replaces it with the REST API documented here.
Deprecation timeline
The legacy API (POST https://5l14kpizah.execute-api.us-east-1.amazonaws.com/prod/apiEvent) is deprecated as of the Platform
API's general availability on October 9, 2026, and
shuts down on April 7, 2027. Legacy API responses carry these headers:
Deprecation: @1791504000
Sunset: Wed, 07 Apr 2027 00:00:00 GMT
Link: <https://www.getproperly.com/developers/migrate>; rel="deprecation"
Your existing legacy x-api-key works on both APIs throughout the window; a key
issued from the web app works on the Platform API only.
What changed
| Legacy API | Platform API |
|---|---|
| Base URL: https://5l14kpizah.execute-api.us-east-1.amazonaws.com/prod | https://platform.getproperly.com |
| One POST /apiEvent with { "property.create": [...], "booking.create": [...] }, up to 20 records batched | One request per record on per-resource routes (POST /v1/properties, POST /v1/bookings, …) |
| HTTP response was an acknowledgement only; real results arrived via webhook | The response body is the result: created resources and their ids return inline (201/200) |
| Per-record validation errors arrived later via webhook | Errors return immediately as HTTP 400 application/problem+json |
| Unknown body fields silently dropped | Unknown fields are rejected; the 400 names each one (code "objectStrict") |
| Out-of-order batches forgiven (the consumer retried until dependencies appeared) | Order matters: create the property first, then use the returned propertyId for pictures, checklists, bookings, and jobs |
| Listings async via webhook (listing.cleaningJobs, listing.propertyListing) | Plain queries returning inline: GET /v1/jobRequests, GET /v1/properties |
| Webhook setup: POST /setupWebhook with replace-all semantics | POST /v1/webhooks plus per-subscription PATCH and DELETE; one signing secret per URL, returned once |
| Webhook payloads unsigned | Signed (Standard Webhooks HMAC). Payload bodies are byte-identical to the legacy API; the signing headers are additive, so an unmodified legacy handler keeps parsing. Verification is new code you add. |
| Missed webhook → support ticket | Self-serve: delivery log (GET /v1/webhooks/{webhookId}/deliveries) and polling backfill (GET /v1/events?after=…) |
| Rate limits: 50 req/s sustained, burst 100, 30,000/day (enforced by the API gateway) | Same numbers, now surfaced via X-RateLimit-* and Retry-After headers |
| x-api-key header | Unchanged: same header, same value |
| jobRequest.create acknowledged; the snapshot arrived by webhook | The job row returns inline in the 201; lifecycle webhooks still arrive later, unchanged |
| Lifecycle webhook payloads | Unchanged shape, now signed |
Legacy event → Platform API route
Field names inside each record are unchanged. Base URL: https://platform.getproperly.com.
Update wire shape
Updates moved from { "<entity>Id": …, "changes": {…} } inside a batch to the id
in the URL and the change fields flat in the body:
{ "property.update": [ { "propertyId": "8jL2mQxT4a", "changes": { "name": "Seaside Loft 2B" } } ] } PATCH /v1/properties/8jL2mQxT4a
{ "name": "Seaside Loft 2B" } Recommended migration path
The legacy and Platform webhook pipelines are independent, so you can run both in parallel and cut over once.
- Create a Platform API key. Create a Platform API key in the web app (API & AI assistants) and use it instead of your legacy key.
- Register a Platform API webhook pointing at a test receiver. Use an endpoint you control for testing. Your legacy webhook configuration stays untouched and keeps firing, so the two pipelines run independently and real events now flow to both. This includes the calls you make through the Platform API: until you remove the legacy webhook at the promote step, your legacy receiver also gets those events, so expect them there while you port.
- Port the client integration. Switch to the new base URL, split batches into per-resource calls, sequence dependent calls, read response bodies for results, move per-record error handling from webhook handlers to HTTP error handling, and remove any unknown or extra body fields.
- Add signature verification. Verify webhook-signature against the raw request bytes using the signing secret returned when you registered the subscription. The verifier on the Webhooks page is ready to paste.
- Validate against live events. Check payload parity, signatures, and retries at your test receiver. Low-volume integrations can synthesize deliveries with POST /v1/webhooks/{webhookId}/test.
- Promote. PATCH /v1/webhooks/{webhookId} flips the subscription url to your production receiver; the signing secret is kept. Remove the legacy webhook configuration in the same window: the legacy POST /setupWebhook replaces the whole configuration on every call, so post the configuration without the old webhook, or an empty list to clear it. The cutover is one-way.
- If something breaks after cutover. Fix the receiver, then backfill from GET /v1/events?after=… and check GET /v1/webhooks/{webhookId}/deliveries. Recovery is forward, not back to the legacy API.