API stability
This API exists because MagicDoor's own applications need it. It is not a versioned product, it can change without notice, and anything you build on it is at your own risk. Read this before you commit to it.
MagicDoor does not offer this API as a product. It is the interface our own applications use, published so that customers who want to automate can. It is not versioned, it carries no compatibility guarantee, and it can change at any time to suit what MagicDoor needs to build next.
If that is not a risk your integration can absorb, do not build on it.
There is no version number anywhere — not in the URL, not in a header, not in the body. There is one live API and you are always on it. So there is no older version to stay on while you catch up, and no window in which two versions run side by side.
Without notice, and without it being a bug, any of this can happen:
- an endpoint changes shape, moves to a different service, or disappears
- a field is renamed, removed, or starts meaning something subtly different
- a new value appears in an enum you were exhaustively matching on
- a status code changes, or a call that used to succeed starts failing validation
- a default changes — a page size, a sort order, an optional field's fallback
We will not usually be doing this to break you. Most changes come from moving functionality out of the original MagicDoor application into smaller services, which is ongoing.
Two different pagination envelopes (one says page, the other currentPage). The same resource served by two hosts. Pages marked deprecated that point at their replacement. That is the migration in progress, visible in the documentation — which is a fair preview of the kind of change to expect.
None of this is a promise. It is a realistic read of where the ground is firmer, to help you decide what to lean on.
| Firmer | Looser |
|---|---|
| The core records — portfolios, properties, units, leases, tenants — and the ids that join them | Anything under a service that is mid-move, especially where the same resource appears twice |
| Conventions: ids as strings, money as decimals, offset-free timestamps | Response envelopes and field names, which differ between the old application and the newer services |
| Reading data | Writing data, where business rules and validation change with the product |
401 on a bad token, 404 on a record you cannot see | Which of 400 / 409 / 422 a particular refusal comes back as |
The difference between an integration that survives a change and one that pages someone at 2am is mostly in how tolerantly it reads.
- Read only the fields you use, and ignore the rest
Do not deserialise into a strict type that rejects unknown properties. New fields appear regularly and are not a breaking change — a strict parser turns every one of them into an outage.
// Default behaviour already ignores unknown members. Do not turn that off: var options = new JsonSerializerOptions { UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, // the default }; - Treat every enum as open
Match the values you care about and have a default branch for everything else. A new lease status or work-order urgency should route to "something I do not handle yet", not throw.
- Never parse an id
Ids are opaque strings. Do not cast them to a number, do not assume a length, do not sort by them. Two endpoints do return ids as JSON numbers — those pages say so, and even there you should keep the value as a string in your own code.
- Do not depend on ordering you were not promised
Where a list's sort order matters to you, sort it yourself. Most endpoint pages here deliberately say nothing about order, because the source does not guarantee one.
- Keep a smoke test you actually run
A handful of calls that assert the shapes you depend on, run on a schedule against production. It is the only way you will find out a response changed before your users do.
# The cheapest possible canary: does a token still work, and does # the field your code reads still exist? curl -sf --url 'https://api.portal.magicdoor.com/company-app/leases?page=1&pageSize=1' \ --header "Authorization: Bearer $MAGICDOOR_TOKEN" \ | jq -e '.items[0].postedBalance != null' > /dev/null \ || echo 'MagicDoor lease shape changed — check before the next sync run' - Make writes recoverable
There is no
Idempotency-Keyheader, so a retriedPOSTcreates a second record. Record what you sent and what came back, so a failed run can be reconciled rather than replayed blindly. See Errors.
No promises, but our intent:
- This reference tracks the API. When something changes, the page changes with it. It is generated from the source, so it is the best available account of how the API behaves today.
- Where an endpoint has moved, its page says so and names the replacement.
- Support will help you if an integration breaks. Include the
traceIdfrom the failing response — see Errors.
What we do not offer: advance notice of changes, a deprecation window, a changelog, a supported client library, or a commitment that anything documented here will exist next month.
Every call acts on real properties, real tenants and real money. There is no test environment to try things against.
Explore with read-only endpoints, and scope your API key to exactly the permissions you need so a mistake cannot reach further than it should — see Authentication.
If you are building something where an unannounced change is not survivable — a product you sell, a migration you cannot repeat, a financial process with a compliance obligation — talk to us before you start rather than after. Write to support@magicdoor.com and describe what you are building.
We would rather know what people depend on than find out by breaking it.