# PropCode API > PropCode’s property reports for properties in NSW, VIC, ACT and TAS, from the same rules, maps and planning documents as the PropCode app. Find a property, unlock it with one of your team’s credits, then read its Pro report (currently NSW CDC only) or write its Rapid Planning Report. Authenticate with an API key from Integrations & API: pc_test_… keys are free on the sample properties, pc_live_… keys unlock any property in those states with your team’s credits. The samples’ Pro reports are free with either. Base URL: `https://app.propcode.com.au/api`. The same docs, rendered: https://app.propcode.com.au/developers. The OpenAPI document: https://app.propcode.com.au/api/v1/openapi.json. ## Two ways in The same features, answers, limits and costs either way. - **REST API:** JSON over HTTPS at `https://app.propcode.com.au/api/v1/…`, described endpoint by endpoint below and in the OpenAPI document. Authenticate with an API key in the `Authorization` header: `Bearer pc_live_…`, or `Bearer pc_test_…` for the sample properties. - **MCP server:** `https://app.propcode.com.au/api/v1/mcp`, streamable HTTP, for AI tools that take an MCP server. One tool per endpoint, limited and charged exactly as the request it makes. Authenticate with the same API key in the same header, or sign in with PropCode (OAuth) from a client that supports it, with no key to copy. ## Quickstart 1. Sign in to PropCode and open **Integrations & API**. Your organisation gets a free test key there. 2. Read a sample property's Pro report. The samples' Pro reports are free with any key and need no unlock: ```bash curl "https://app.propcode.com.au/api/v1/report?title=5008/DP752015" \ -H "Authorization: Bearer pc_test_…" ``` 3. To check any property, make a live key in **Integrations & API** and swap it in. Unlock a property before reading its reports, which spends one of your team's credits, as unlocking in PropCode does: ```bash curl -X POST "https://app.propcode.com.au/api/v1/unlocks" \ -H "Authorization: Bearer pc_live_…" \ -H "Content-Type: application/json" \ -d '{ "title": "", "spendCredit": true }' ``` Nothing else in your code changes. ## Keys Send your key on every request: `Authorization: Bearer <key>`. Never put a key in a URL: a request with one gets `key_in_url`. | Key | Test (`pc_test_…`) | Live (`pc_live_…`) | |---|---|---| | Who makes it | Anyone in the organisation | Anyone in the organisation | | Properties | The 3 samples | Any in NSW, VIC, ACT or TAS | | Unlocks | Free | Spend your team's credits | | Samples' Pro reports | Free, no unlock | Free, no unlock | | Rapid Planning Reports | Not generated | Generated and read | | Shown | Any time, in Integrations & API | Once, when it's made | A key acts as the person who made it. Its unlocks and reports are theirs, as if they'd made them in PropCode, and on Max it holds their seat. If they leave your team, its unlocks and reports stop with `key_owner_left`; make a new key. A live key is revoked by the person who made it, or by an owner or admin. Revoke a key in **Integrations & API › Keys**. It stops working at once. ## Title ids Every answer names its property by its title id and its `jurisdiction` (`nsw`, `vic`, `act` or `tas`): together, the key to use in your own records, since the same id can name a property in two states. Find one with `GET /v1/properties`, by address or by point, and pass both back. `jurisdiction` is `nsw` when you leave it out. | State | Form | Example | |---|---|---| | NSW | Lot and plan | `5008/DP752015` | | NSW | Lot, section and plan | `10/10/DP979849` | | NSW | A whole strata plan | `SP58841` | | NSW | Several lots, joined with + | `10/DP503890+11/DP503890` | | VIC | Lot and plan | `1001\PS340516` | | ACT | Suburb, block and section | `ACTON Block 11 Section 67` | | TAS | Volume and folio, or CID or PID | `Volume 100007 Folio 1` | NSW plans are `DP` (deposited) or `SP` (strata), and each lot appears once in an id. A VIC id's backslash is `\\` in JSON, and a query string encodes it as `%5C`. Ask for several NSW lots in any order. If PropCode hasn't seen that combination, a live key's first ask combines them into one property, and the lots must lie within 10 km of each other. The answer's `title` is the id to keep, with its lots in PropCode's order. ## Sample properties Test keys answer for these properties, with real answers: - `5008/DP752015`: 32 Holden Street, Maroubra. The property in our sample reports: about half the codes eligible. - `10/10/DP979849`: 24 Hercules Street, Goulburn. Rural, under the Inland Code: no code eligible. - `10/DP503890+11/DP503890`: 80 Showground Road, Gosford. Two lots in a business zone, zoned by a SEPP: few codes eligible. ## Credits - Unlocking a property spends one of your team's credits, as unlocking it in PropCode does. It takes `spendCredit: true`, so nothing spends by accident: an AI agent should ask its user first. - An unlock names the property by its title id. Look the address up with `GET /v1/properties` first, so a credit is never spent on the wrong match. - `creditType` picks the credit. `pro`, the default, is Pro, as in PropCode: a deep dive into the granular planning rules per land use and approval pathway. Through the API, that's the property's Pro report (currently NSW CDC only) and its Rapid Planning Report. - `cdc` and `rpr` spend the credits of a plan from before Core and Pro: a CDC report credit unlocks the property's CDC report, and a Rapid Planning Report credit its Rapid Planning Report. - Once your team has unlocked a property, it's unlocked for everyone in the team, in PropCode too. Unlocking it again is free, and so are its reports: reading them, and generating its Rapid Planning Reports, of every kind and for every land use. - Pro credits are your team's Property Packs and Max, bought in PropCode by an admin. With Max, an unlock comes from its daily allowance first (`dailyAllowance`, shared by the team's seats and starting again at `resetsAt`), then from packs. A pack set to auto-refill when it runs out refills the same way when the API spends its last credit. - `GET /v1/credits` says what's left of each type, and so does every unlock. - `GET /v1/unlocks` lists the properties your team has unlocked, newest first: what each is unlocked as, who unlocked it and when, and its Rapid Planning Reports' ids and share links, so a property or a report is never lost with its id. - The sample properties' Pro reports and map measures (such as height limits) are free with any key, test or live, and need no unlock. Their Rapid Planning Reports need a live key and an unlock, as any property's do. - A Rapid Planning Report citation's `contentUrl` gives the text it points to, from the version the report read (`GET /v1/citations/…`). Reading it is free. - Property lookups, searches and the lists of land uses and codes are free. ## Limits Limits are per organisation, shared by all its live keys: | Limit | Live keys | |---|---| | Requests a second | 10 | | Pro reports being worked out at once | 4 | | Different properties looked up a day (GET /v1/property) | 2,000 | | Address searches a day | 300 | | New multi-lot titles an hour | 60, joining 100 lots at most | | Different cited passages read a day (GET /v1/citations) | 2,000 | Address searches also share a daily allowance across every organisation, and test keys a smaller share of it. Once it's spent, they get 429 until midnight US Pacific. New multi-lot titles share an hourly allowance across every organisation too. Looking a property up by title id or by point is never limited this way. Over a limit, a request gets 429 with a `Retry-After` header saying when to retry. Need more? Ask us. Test keys are for trying the API on the samples: 5 requests a second and 100 a day, across the organisation's test keys. ## Errors Every error is JSON with one shape: ```json { "error": { "type": "billing", "code": "no_credits", "message": "…", "requestId": "req_…", "docUrl": "https://app.propcode.com.au/developers/errors#no_credits" } } ``` `type` sets the HTTP status; `code` says exactly what went wrong. [Every code](https://app.propcode.com.au/developers/errors), with what to do about it. Quote `requestId`, also sent as the `Request-Id` header, if you contact us. When your team has nothing to unlock a property with, `not_unlocked`, `no_credits` and `max_daily_limit` say who buys credits. For your team's owner or an admin they add `choices`: what they can buy in PropCode, each with its price and a checkout link. Show the person the links; they buy there. `GET /v1/credits` gives the same, as `choices` and `note`, when your team has nothing left. ## Feedback Found a bug, need something PropCode doesn't do, or have anything else to tell us? `POST /v1/feedback` (MCP: `send_feedback`) with a `kind` (`bug`, `feature_request` or `feedback`) and a one-line `summary`. Add `details`, the `requestId` of the request it's about, and the property or report, when there is one. A person at PropCode reads every one, and the answer is always "Feedback logged.". It's free, with test and live keys alike, up to 50 a day per organisation. ## MCP AI tools that take an MCP server can use the API directly: Claude Code, Codex, Cursor, VS Code and agent frameworks. The server is at `https://app.propcode.com.au/api/v1/mcp` (streamable HTTP) and takes your key in the same header. Its tools mirror the endpoints, one each: `search_properties`, `get_property`, `get_credits`, `unlock_property`, `read_report`, `generate_rpr`, `read_rpr`, `read_citation`, `list_unlocked_properties`, `list_land_uses`, `list_codes` and `send_feedback`. A tool call is limited, and spends credits, exactly as the request it makes. `unlock_property` takes `spendCredit` too, so an agent asks its user before spending. **Sign in with PropCode instead of a key.** An MCP client that supports sign-in (OAuth) can take the server’s address alone and open PropCode's sign-in, so there's no key to copy. Signed in, the tools act for you in your current PropCode team, with its credits. `unlock_property` is the only tool that spends anything: one credit your team already has, and only with `spendCredit`, so the agent asks you first. Credits are bought in PropCode, never through the API or MCP. ## Versions The API is versioned in its path: `/api/v1`. Within v1 we only add: new endpoints, new fields and new error codes, so ignore fields you don't know. Lists of values grow too, such as the report's pathways and verdicts: read a value you don't know as its field says, and treat a land use or pathway the report leaves out as unanswered, never as a no. A rename or removal would be `/v2`, announced first. Reports come from PropCode's current rules, maps and planning documents, as in PropCode itself. When those change, a report can change too. **Deprecated:** `GET /v1/reports`, the first name of `GET /v1/unlocks`, answers the same until 30 November 2026, then goes. Its MCP tool `list_reports` is gone: call `list_unlocked_properties`. ## Endpoints ### GET /api/v1/properties Search properties. Finds properties in NSW, VIC, ACT and TAS by street address, or by the point at lat and lng. Each has its title id and its jurisdiction, which the other endpoints take together. Free: it isn’t billed. Query parameters: - `address`: A street address, e.g. 32 Holden Street, Maroubra NSW. It must match one property: otherwise the answer is 409 with candidates. - `lat`: Latitude, with lng: the property at this point. - `lng`: Longitude, with lat. Responses (every error has the Error body): - 200: The properties found, best first. Body: `PropertyList`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`, `invalid_address`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 404: Not found. Codes: `address_not_found`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`, `daily_search_limit`. - 500: Our error. Codes: `internal_error`. ### GET /api/v1/property Get a property's zones and overlays. One property's planning maps: its zone, controls and overlays, as PropCode's property page lists them. No analysis. Their measures, such as height and floor space ratio limits, come once your team has unlocked the property as Pro. Not billed. Query parameters: - `title` (required): The title id, e.g. 5008/DP752015. Find one with GET /v1/properties. - `jurisdiction`: The state: nsw (the default), vic, act or tas. Complying development answers are NSW only. Responses (every error has the Error body): - 200: The property and its maps. Body: `PropertyMaps`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 403: This key can’t ask that. Codes: `sandbox_title_only`. - 404: Not found. Codes: `title_not_found`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. An example 200: ```json { "object": "property", "title": "5008/DP752015", "jurisdiction": "nsw", "address": "32 Holden Street, Maroubra NSW 2035", "lga": "Randwick", "lotAreaM2": 651.3, "unlocked": true, "access": "free_sample", "maps": [ { "name": "Land Zoning Map", "features": [ { "coverage": 1, "attributes": [ { "label": "Instrument", "value": "Randwick LEP 2012" }, { "label": "Zone", "value": "R2 - Low Density Residential" } ] } ] }, { "name": "Height of Buildings Map", "features": [ { "coverage": 1, "attributes": [ { "label": "Maximum Building Height (m)", "value": "9.5 m" } ] } ] } ] } ``` ### GET /api/v1/credits Get your credits. What your team has to unlock properties with, by credit type: Pro credits from Property Packs, Max's daily allowance (dailyAllowance) when it has Max, and the CDC report and Rapid Planning Report credits of a plan from before Core and Pro. Free. Responses (every error has the Error body): - 200: Your team's credits. Body: `Credits`. - 400: The request isn’t valid. Codes: `key_in_url`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 403: This key can’t ask that. Codes: `key_owner_left`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. An example 200: ```json { "object": "credits", "pro": { "remaining": 3 }, "dailyAllowance": { "limit": 40, "used": 12, "remaining": 28, "resetsAt": "2026-10-27T13:00:00.000Z" }, "cdc": { "remaining": 0 }, "rpr": { "remaining": 0 } } ``` ### POST /api/v1/unlocks Unlock a property. Unlocks a property for your team, as unlocking it in PropCode does, by its title id: look the address up with GET /v1/properties first, so a credit is never spent on the wrong match. creditType picks the credit: pro (the default) gives the Pro report (currently NSW CDC only) and the Rapid Planning Report (outside NSW, the Rapid Planning Report alone), spending Max's daily allowance first, then your Property Packs; cdc and rpr spend the credits of a plan from before Core and Pro, for the CDC report (NSW) or the Rapid Planning Report. It spends one credit, and only spendCredit: true lets it. A property your team has already unlocked costs nothing. Test keys unlock the samples without spending. JSON body: - `title` (string): The title id, from GET /v1/properties. Look the address up first, so a credit is never spent on the wrong match. - `jurisdiction` (string, optional): The state: nsw (the default), vic, act or tas. Complying development answers are NSW only. - `creditType` ("pro" | "cdc" | "rpr", optional): The credit to spend, from GET /v1/credits. pro (the default): the Pro report (currently NSW CDC only) and the Rapid Planning Report. cdc: the CDC report, with a CDC report credit from a plan from before Core and Pro. rpr: the Rapid Planning Report, with a Rapid Planning Report credit from such a plan. - `spendCredit` (true): true: you agree to spend one credit on this property if your team hasn't unlocked it yet. An agent asks its user first. For example: ```json { "title": "5008/DP752015", "spendCredit": true } ``` Responses (every error has the Error body): - 200: The property, unlocked. Body: `Unlock`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`, `not_eligible`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 402: Billing stops this answer. Codes: `no_credits`, `max_daily_limit`. - 403: This key can’t ask that. Codes: `sandbox_title_only`, `key_owner_left`. - 404: Not found. Codes: `title_not_found`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`, `unlock_failed`. An example 200: ```json { "object": "unlock", "property": { "title": "5008/DP752015", "jurisdiction": "nsw" }, "product": "pro", "spent": true, "paidWith": "daily_allowance", "credits": { "object": "credits", "pro": { "remaining": 3 }, "dailyAllowance": { "limit": 40, "used": 13, "remaining": 27, "resetsAt": "2026-10-27T13:00:00.000Z" }, "cdc": { "remaining": 0 }, "rpr": { "remaining": 0 } } } ``` ### GET /api/v1/unlocks List unlocked properties. The properties your team has unlocked, newest first, as PropCode's report history lists them: what each is unlocked as, who unlocked it and when, and its Rapid Planning Reports with their ids and share links. How to find a property or a report again without its id. Filter by address or title id, by what it was unlocked as, or to the properties the key's owner unlocked (mine). Test keys list nothing. Free. Query parameters: - `address`: Only properties whose address or title id contains this. - `product`: Only properties your team unlocked as this: pro, or the cdc or rpr credits of a plan from before Core and Pro. - `mine`: true: only properties the key's owner unlocked. Otherwise every property your team unlocked. Either way, each property shows everything your team holds on it: its products, who last unlocked it, and all its Rapid Planning Reports. - `page`: From 1. - `limit`: Properties a page, up to 100. Responses (every error has the Error body): - 200: Your team's unlocked properties, a page at a time. Body: `UnlockedPropertyList`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 403: This key can’t ask that. Codes: `key_owner_left`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. An example 200: ```json { "object": "list", "data": [ { "object": "unlocked_property", "property": { "title": "5008/DP752015", "jurisdiction": "nsw", "address": "32 Holden Street, Maroubra NSW 2035" }, "products": [ "pro" ], "unlockedAt": "2026-10-26T01:52:40.000Z", "unlockedBy": "sam@example.com", "shareUrl": "https://app.propcode.com.au/share/m3n7b1v5c8x2", "rprs": [ { "id": "7c1d0f9e-2b54-4c1a-9a63-5f0e8d2b4a71", "reportType": "multiple_land_uses", "createdAt": "2026-10-26T01:55:10.000Z", "status": "ready", "generatedAt": "2026-10-26T01:58:02.000Z", "createdBy": "sam@example.com", "shareUrl": "https://app.propcode.com.au/share/q4w8e2r6t9y3" } ] } ], "page": 1, "limit": 20, "total": 1 } ``` ### GET /api/v1/reports List unlocked properties (old name). Deprecated: the first name of GET /v1/unlocks, which answers the same. It's removed after 30 November 2026. Query parameters: - `address`: Only properties whose address or title id contains this. - `product`: Only properties your team unlocked as this: pro, or the cdc or rpr credits of a plan from before Core and Pro. - `mine`: true: only properties the key's owner unlocked. Otherwise every property your team unlocked. Either way, each property shows everything your team holds on it: its products, who last unlocked it, and all its Rapid Planning Reports. - `page`: From 1. - `limit`: Properties a page, up to 100. Responses (every error has the Error body): - 200: Your team's unlocked properties, a page at a time. Body: `UnlockedPropertyList`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 403: This key can’t ask that. Codes: `key_owner_left`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. An example 200: ```json { "object": "list", "data": [ { "object": "unlocked_property", "property": { "title": "5008/DP752015", "jurisdiction": "nsw", "address": "32 Holden Street, Maroubra NSW 2035" }, "products": [ "pro" ], "unlockedAt": "2026-10-26T01:52:40.000Z", "unlockedBy": "sam@example.com", "shareUrl": "https://app.propcode.com.au/share/m3n7b1v5c8x2", "rprs": [ { "id": "7c1d0f9e-2b54-4c1a-9a63-5f0e8d2b4a71", "reportType": "multiple_land_uses", "createdAt": "2026-10-26T01:55:10.000Z", "status": "ready", "generatedAt": "2026-10-26T01:58:02.000Z", "createdBy": "sam@example.com", "shareUrl": "https://app.propcode.com.au/share/q4w8e2r6t9y3" } ] } ], "page": 1, "limit": 20, "total": 1 } ``` ### GET /api/v1/report Read the planning report. An unlocked property's planning report (in PropCode, the Pro report): for each land use, whether it can be built under each pathway and by which way, each pathway's rules, and, on a property your team holds Pro on, how sure each answer is. It covers complying development in NSW (nsw-cdc) for now, so another state's jurisdiction answers unsupported_jurisdiction, and new pathways arrive as new entries in the same shape. Give landUse to narrow it to one land use and its rules. A rule's contentUrl gives its clause's text (GET /v1/citations/…). A Pro or a CDC report unlock reads it. Free once the property is unlocked. Query parameters: - `title` (required): The property's title id, as GET /v1/properties gives it, with its jurisdiction. NSW: lot/plan (5008/DP752015), lot/section/plan (10/10/DP979849) or a strata plan (SP58841); for several lots, join their ids with +: 10/DP503890+11/DP503890. VIC: lot\plan (1001\PS340516). ACT: suburb, block and section (ACTON Block 11 Section 67). TAS: volume and folio (Volume 100007 Folio 1), or CID or PID. - `jurisdiction`: The state: nsw (the default), vic, act or tas. Complying development answers are NSW only. - `landUse`: A land use's id, or up to 10 separated by commas, from GET /v1/land-uses, each one a pathway can deliver: the report narrows to them, their answers and the rules behind them. - `view`: summary: each land use's verdict, with only its eligible ways and alterations and their figures, and no rules unless rules is true: about a quarter of the full report. full, the default: every way and alteration, with the rules. - `rules`: false: leave out each pathway's rules, for the answers alone, a much smaller report. Rules come unless false; with view=summary, only when true. Responses (every error has the Error body): - 200: The report. Body: `Report`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 403: This key can’t ask that. Codes: `sandbox_title_only`, `key_owner_left`, `not_unlocked`. - 404: Not found. Codes: `title_not_found`. - 422: Facts or jurisdiction not accepted. Codes: `unsupported_jurisdiction`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. - 503: Busy: retry after Retry-After. Codes: `engine_busy`. An example 200: ```json { "object": "report", "property": { "title": "5008/DP752015", "jurisdiction": "nsw", "address": "32 Holden Street, Maroubra NSW 2035", "lga": "Randwick", "lotAreaM2": 651.3 }, "evaluatedAt": "2026-10-28T02:11:16.147Z", "pathways": [ { "id": "nsw-cdc", "name": "Complying development", "rules": [ { "id": "cdc-1-18-1-b", "ways": [ "dual-occupancy-detached" ], "reference": "1.18(1)(b)", "instrument": "State Environmental Planning Policy (Exempt and Complying Development Codes) 2008", "description": "Has permissibility with consent for dual occupancy (detached)?", "result": "met", "certainty": "best_guess" }, { "id": "cdc-3b-2-d", "ways": [ "dual-occupancy-detached" ], "reference": "3B.2(d)", "instrument": "State Environmental Planning Policy (Exempt and Complying Development Codes) 2008", "description": "Does not currently have a secondary dwelling or group home?", "result": "unknown", "certainty": "not_known" } ] } ], "landUses": [ { "id": "dual-occupancy-detached", "name": "Dual occupancy (detached)", "pathways": [ { "pathway": "nsw-cdc", "verdict": "eligible", "ways": [ { "id": "dual-occupancy-detached", "name": "Dual occupancy (detached)", "group": "Low Rise Housing Diversity Code", "verdict": "eligible", "applies": true, "conclusions": { "dwellings": { "min": 2, "max": 2 }, "maxGfaM2": 462.825, "maxHeightM": 8.5, "minSubdivisionLotM2": 225 } } ], "alterations": [ { "id": "housing-alterations-code", "name": "Minor residential alterations", "group": "Housing Alterations Code", "verdict": "not_eligible", "applies": true } ] } ] } ], "keyControls": [ { "name": "landZone", "title": "Land Zone", "value": "R2" }, { "name": "heightOfBuildingsEPI", "title": "Max Height (EPI)", "value": 9.5, "unit": "m" }, { "name": "floorSpaceRatioEPI", "title": "Max FSR (EPI)", "value": 0.5, "unit": ":1" }, { "name": "minimumLotSizeEPI", "title": "Min. Lot Size (EPI)", "value": 275, "unit": "m²" }, { "name": "lotArea", "title": "Total Lot Area", "value": 651.3, "unit": "m²" }, { "name": "inBushFireProneLand", "title": "In Bushfire Prone Land?", "value": false }, { "name": "isFloodControlLot", "title": "Is Flood Control Lot?", "value": null } ], "unknownCriticalFacts": [], "reportUrl": "https://app.propcode.com.au/cdc/nsw/5008%2FDP752015" } ``` ### POST /api/v1/reports/rpr Generate the Rapid Planning Report. Starts generating an unlocked property's Rapid Planning Report, of the kind reportType names: every land use, one land use (landUse), or subdivision. It's generated from the property's planning documents, in NSW, VIC, ACT and TAS, and takes a few minutes, so this answers at once with its id; read GET /v1/reports/rpr/{id} until it's ready. Once the property is unlocked (Pro, or a Rapid Planning Report credit), its Rapid Planning Reports cost nothing more: every kind, for every land use. Your team holds one report of each kind on a property, and one for each land use, generated the first time anyone in the team asks for it. Asking again, with any of the team's keys or in PropCode, answers with that report (alreadyGenerated: true), unless it's incomplete or failed, which generates it again (once a day at most). Live keys only. Query parameters: - `section`: One section, by its number in outline, or none for the outline alone. Every section unless given. JSON body: - `title` (string): The property's title id, as GET /v1/properties gives it, with its jurisdiction. NSW: lot/plan (5008/DP752015), lot/section/plan (10/10/DP979849) or a strata plan (SP58841); for several lots, join their ids with +: 10/DP503890+11/DP503890. VIC: lot\plan (1001\PS340516). ACT: suburb, block and section (ACTON Block 11 Section 67). TAS: volume and folio (Volume 100007 Folio 1), or CID or PID. - `jurisdiction` (string, optional): The state: nsw (the default), vic, act or tas. Complying development answers are NSW only. - `reportType` ("multiple_land_uses" | "single_land_use" | "subdivision", optional): multiple_land_uses (the default): the rules for every land use. single_land_use: the rules for one land use, named by landUse. subdivision: the rules for subdividing the lot. - `landUse` (string, optional): A land use's id, from GET /v1/land-uses: required for single_land_use, and taken by no other type. For example: ```json { "title": "5008/DP752015", "reportType": "single_land_use", "landUse": "secondary-dwelling" } ``` Responses (every error has the Error body): - 200: The report, generating or ready. Body: `Rpr`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 403: This key can’t ask that. Codes: `sandbox_title_only`, `key_owner_left`, `not_unlocked`, `live_key_required`. - 404: Not found. Codes: `title_not_found`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. An example 200: ```json { "object": "rpr", "id": "7c1d0f9e-2b54-4c1a-9a63-5f0e8d2b4a71", "property": { "title": "5008/DP752015", "jurisdiction": "nsw", "address": "32 Holden Street, Maroubra NSW 2035" }, "reportType": "multiple_land_uses", "status": "generating", "createdAt": "2026-10-26T01:55:10.000Z", "reportUrl": "https://app.propcode.com.au/g-report/q4w8e2r6t9y3", "shareUrl": "https://app.propcode.com.au/share/q4w8e2r6t9y3" } ``` ### GET /api/v1/reports/rpr/{id} Read a Rapid Planning Report. A Rapid Planning Report your team started: generating, ready, incomplete or failed, with its outline and sections once it has them. A whole report runs to tens of thousands of characters, so read the outline (section=none), then a section at a time (section=2). An incomplete or failed report is generated again by POST /v1/reports/rpr, once a day at most. Query parameters: - `id` (required): The id POST /v1/reports/rpr answered with. - `section`: One section, by its number in outline, or none for the outline alone. Every section unless given. Responses (every error has the Error body): - 200: The report. Body: `Rpr`. - 400: The request isn’t valid. Codes: `key_in_url`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 403: This key can’t ask that. Codes: `key_owner_left`. - 404: Not found. Codes: `not_found`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. An example 200: ```json { "object": "rpr", "id": "7c1d0f9e-2b54-4c1a-9a63-5f0e8d2b4a71", "property": { "title": "5008/DP752015", "jurisdiction": "nsw", "address": "32 Holden Street, Maroubra NSW 2035" }, "reportType": "multiple_land_uses", "status": "ready", "createdAt": "2026-10-26T01:55:10.000Z", "reportUrl": "https://app.propcode.com.au/g-report/q4w8e2r6t9y3", "shareUrl": "https://app.propcode.com.au/share/q4w8e2r6t9y3", "outline": [ { "section": 1, "title": "Maximum Building Height", "characters": 184 } ], "sections": [ { "title": "Maximum Building Height", "html": "<p>The maximum building height for this property is <strong>9.5 m</strong> under clause 4.3 of the Randwick Local Environmental Plan 2012 [1], shown on its Height of Buildings Map.</p>", "citations": [ { "n": 1, "document": "Randwick Local Environmental Plan 2012", "location": "Part 4 Principal development standards › 4.3 Height of buildings", "libraryUrl": "https://app.propcode.com.au/library/document/nsw/epi-2013-0036?version=2026-07-01#idx-1309", "contentUrl": "https://app.propcode.com.au/api/v1/citations/nsw/epi-2013-0036/2026-07-01/0/1309" } ] } ] } ``` ### GET /api/v1/citations/{jurisdiction}/{document}/{version}/{file}/{passageId} Read a citation. The text a Rapid Planning Report citation points to, from the version the report read: GET its contentUrl. It answers with the cited passage, its text stopping at 10,000 characters (truncated says so). With includeParentSection=true it answers with the section the passage is in instead, with all its subclauses and the passage in <mark>, as the Library shows a citation; parentSection says which you got, since it's the passage alone where the section would be the whole document, runs past 20,000 characters, or is the passage itself. Each comes as HTML and as plain text. A report cites 50 to 100 passages, so read the ones you need. Any key reads them; a live key's team reads up to 2,000 different passages a day, and repeats are free. Legislation's text is the published legislation's; a development control plan's is read from the council's PDF, which libraryUrl opens. Query parameters: - `jurisdiction` (required): The state the document is in: nsw, vic, act or tas. - `document` (required): The document's id in PropCode's Library. - `version` (required): The document's version the report read, e.g. 2026-07-01. - `file` (required): The document's file: 0 for most legislation; a DCP has several. - `passageId` (required): The cited passage's id within the file, as a citation's contentUrl gives it: PropCode's own, not a clause number. - `includeParentSection`: true: the section the passage is in, with all its subclauses and the passage in <mark>, as the Library shows a citation, in place of the passage alone. parentSection says which you got: it's the passage alone where the section would be the whole document, runs past 20,000 characters, or is the passage itself. Responses (every error has the Error body): - 200: The cited text. Body: `Citation`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`, `passage_too_broad`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 404: Not found. Codes: `citation_not_found`. - 422: Facts or jurisdiction not accepted. Codes: `unsupported_jurisdiction`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`, `daily_citation_limit`. - 500: Our error. Codes: `internal_error`. An example 200, the passage (the default): ```json { "object": "citation", "document": { "id": "epi-2013-0036", "name": "Randwick Local Environmental Plan 2012", "jurisdiction": "nsw" }, "version": "2026-07-01", "file": 0, "passageId": 1309, "location": "Part 4 Principal development standards › 4.3 Height of buildings", "parentSection": false, "html": "<div><span class=\"identifier\">(2)</span> The height of a building on any land is not to exceed the maximum height shown for the land on the <em>Height of Buildings Map</em>.</div>", "text": "(2) The height of a building on any land is not to exceed the maximum height shown for the land on the Height of Buildings Map.", "libraryUrl": "https://app.propcode.com.au/library/document/nsw/epi-2013-0036?version=2026-07-01#idx-1309" } ``` An example 200, with includeParentSection=true: ```json { "object": "citation", "document": { "id": "epi-2013-0036", "name": "Randwick Local Environmental Plan 2012", "jurisdiction": "nsw" }, "version": "2026-07-01", "file": 0, "passageId": 1309, "location": "Part 4 Principal development standards › 4.3 Height of buildings", "parentSection": true, "html": "<div><h3><span class=\"identifier\">4.3</span> Height of buildings</h3><div><span class=\"identifier\">(1)</span> <p>The objectives of this clause are as follows—</p><ol><li><span class=\"identifier\">(a)</span> to ensure that the size and scale of development is compatible with the desired future character of the locality,</li><li><span class=\"identifier\">(b)</span> to ensure that development is compatible with the scale and character of contributory buildings in a conservation area or near a heritage item,</li><li><span class=\"identifier\">(c)</span> to ensure that development does not adversely impact on the amenity of adjoining and neighbouring land in terms of visual bulk, loss of privacy, overshadowing and views.</li></ol></div><mark><div><span class=\"identifier\">(2)</span> The height of a building on any land is not to exceed the maximum height shown for the land on the <em>Height of Buildings Map</em>.</div></mark><div><span class=\"identifier\">(2A)</span> Despite subclause (2), the maximum height of a dwelling house or semi-detached dwelling on land in Zone R3 Medium Density Residential is 9.5 metres.</div></div>", "text": "4.3 Height of buildings\n(1) The objectives of this clause are as follows—\n(a) to ensure that the size and scale of development is compatible with the desired future character of the locality,\n(b) to ensure that development is compatible with the scale and character of contributory buildings in a conservation area or near a heritage item,\n(c) to ensure that development does not adversely impact on the amenity of adjoining and neighbouring land in terms of visual bulk, loss of privacy, overshadowing and views.\n(2) The height of a building on any land is not to exceed the maximum height shown for the land on the Height of Buildings Map.\n(2A) Despite subclause (2), the maximum height of a dwelling house or semi-detached dwelling on land in Zone R3 Medium Density Residential is 9.5 metres.", "libraryUrl": "https://app.propcode.com.au/library/document/nsw/epi-2013-0036?version=2026-07-01#idx-1309" } ``` ### GET /api/v1/land-uses List the land uses. Every land use in a state, as Rapid Planning Reports cover them, with its id: the landUse of a single land use’s Rapid Planning Report, and of the planning report where a pathway can deliver it. Ids are stable: they never change, and no id is used for another land use. pathways lists the pathways that can deliver each, with the ids of their ways: for nsw-cdc, the complying development codes (NSW only). Free. Query parameters: - `jurisdiction`: The state whose land uses to list, such as nsw or vic. Responses (every error has the Error body): - 200: The land uses. Body: `LandUseList`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. An example 200: ```json { "object": "list", "data": [ { "id": "secondary-dwelling", "name": "Secondary dwelling", "category": "Residential & Accommodation", "popular": true, "pathways": [ { "pathway": "nsw-cdc", "wayIds": [ "secondary-dwelling", "housing-alterations-code" ] } ] }, { "id": "agritourism", "name": "Agritourism", "category": "Agriculture & Resources", "popular": false, "pathways": [] } ] } ``` ### GET /api/v1/codes List the codes. The complying development codes the Pro report answers for, in the report’s order. Complying development is NSW only. Responses (every error has the Error body): - 200: The codes. Body: `CodeList`. - 400: The request isn’t valid. Codes: `key_in_url`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`. - 500: Our error. Codes: `internal_error`. ### POST /api/v1/feedback Send feedback. Tells PropCode about a bug, a feature you or your user need, or anything else, such as an answer that looks wrong. A person at PropCode reads every one; the answer is always "Feedback logged.", with no reply through the API. Send one whenever something doesn't work as you expected. Include the requestId of the request it's about, from its error or its Request-Id header. Anything shaped like an API key is removed before it's stored. Test and live keys alike, up to 50 a day per organisation. Free. JSON body: - `kind` ("bug" | "feature_request" | "feedback"): bug: something that doesn't work. feature_request: something you or your user need that PropCode doesn't do. feedback: anything else, such as an answer that looks wrong, or what worked. - `summary` (string): One line, up to 120 characters. - `details` (string, optional): Optional. Up to 4,000 characters of plain text: the steps, what you expected and what happened. - `requestId` (string, optional): Optional. The id of the request it's about, from an error's requestId or a Request-Id header. - `property` (object, optional): Optional. The property it's about. - `title` (string): The property's title id, as GET /v1/properties gives it, with its jurisdiction. NSW: lot/plan (5008/DP752015), lot/section/plan (10/10/DP979849) or a strata plan (SP58841); for several lots, join their ids with +: 10/DP503890+11/DP503890. VIC: lot\plan (1001\PS340516). ACT: suburb, block and section (ACTON Block 11 Section 67). TAS: volume and folio (Volume 100007 Folio 1), or CID or PID. - `jurisdiction` (string, optional): The state: nsw (the default), vic, act or tas. Complying development answers are NSW only. - `reportId` (string, optional): Optional. The id of the report or Rapid Planning Report it's about. - `client` (string, optional): Optional. Your app's name and version, e.g. acme-planner 2.3.0. For example: ```json { "kind": "bug", "summary": "read_rpr fails with 500 for a VIC title", "details": "Twice in a row, 10 minutes apart. Other VIC titles work.", "requestId": "req_8f3K2pQx7LmN0aZc", "property": { "title": "1\\TP618311", "jurisdiction": "vic" } } ``` Responses (every error has the Error body): - 201: Logged. Body: `Feedback`. - 400: The request isn’t valid. Codes: `key_in_url`, `invalid_parameter`. - 401: No valid key. Codes: `missing_api_key`, `invalid_api_key`. - 422: Facts or jurisdiction not accepted. Codes: `unsupported_jurisdiction`. - 429: Over a limit: retry after Retry-After. Codes: `rate_limited`, `sandbox_daily_limit`, `feedback_limit_reached`. - 500: Our error. Codes: `internal_error`. An example 201: ```json { "object": "feedback", "message": "Feedback logged." } ``` ## Response fields ### Property A property: a title, and where it is. - `title` (string): The title reference. - `jurisdiction` (string): The state: nsw, vic, act or tas. - `address` (string): The property's street address. - `lga` (string, optional): The local government area. - `lotAreaM2` (number, optional): The lot's area, in m². ### PropertyMatch A property a search found. - `title` (string): The title reference. - `jurisdiction` (string): The state: nsw, vic, act or tas. - `address` (string): The property's street address. - `lga` (string, optional): The local government area. - `lotAreaM2` (number, optional): The lot's area, in m². - `match` (object): How the search found this property. - `kind` ("contains" | "address" | "nearest"): How it was found: the lot contains the point, carries the address, or is the nearest. - `distanceM` (number): How far the lot is from the point, in m. - `confidence` ("high" | "medium" | "low", optional): Address searches: how sure the address match is. ### PropertyList - `object` ("list") - `data` (PropertyMatch[]) ### MapFeature One feature of a map that touches the lot. - `coverage` (number | null): How much of the lot it covers, from 0 to 1, when known. Null until your team has unlocked the property as Pro. - `attributes` (object[]): What the map says here, as the map labels it, e.g. Zone: R2 - Low Density Residential. ### Map A planning map, with its features that touch the lot. - `name` (string): The map, e.g. Land Zoning Map. - `features` (MapFeature[]) ### PropertyMaps A property with its zones and overlays. - `title` (string): The title reference. - `jurisdiction` (string): The state: nsw, vic, act or tas. - `address` (string): The property's street address. - `lga` (string, optional): The local government area. - `lotAreaM2` (number, optional): The lot's area, in m². - `object` ("property") - `unlocked` (boolean): Whether your team has unlocked the property as Pro, or it is one of the sample properties, whose measures and Pro report are free with any key. Until then, the maps leave out their measures, such as height and floor space ratio limits and minimum lot sizes, and how much of the lot each feature covers. access says which. - `access` ("unlocked" | "free_sample" | "locked"): How your team reads the property's measures and Pro report: unlocked, where it holds the property as Pro; free_sample, for one of the sample properties, free with any key and never among your unlocked properties; locked until a Pro unlock. - `maps` (Map[]): The planning maps with a feature on the lot, as PropCode's property page lists them: its zone, its controls and its overlays. ### Verdict The answer, in its pathway's words. For complying development (nsw-cdc): eligible: on the facts PropCode holds, the development meets this code's requirements as complying development, and any rule waiting on a fact it can't settle shows as unknown. not_eligible: it fails a requirement of this code. no_conclusion: a fact PropCode can't determine decides it; see unknownCriticalFacts. Other pathways may add their own values: read one you don't know as no_conclusion. One of: string. ### SummaryConclusions What the code allows on this lot, where it says. - `dwellings` (number | string, optional): How many dwellings the code allows. - `maxGfaM2` (number, optional): The largest gross floor area allowed, in m². - `maxHeightM` (number, optional): The greatest height, in m. - `minSubdivisionLotM2` (number | null, optional): The smallest lot a subdivision may make, in m²; null when the lot can't be subdivided under this code. ### SummaryWay One way to deliver a land use: a code's answer for it. - `code` (object): The complying development code. - `id` (string): The code's id, as GET /v1/codes lists it. - `name` (string): The code's name. - `group` (string): The group the code belongs to. - `verdict` (Verdict) - `applies` (boolean | null): Whether the code applies to this lot; null while a critical fact is unknown. - `conclusions` (SummaryConclusions, optional) ### SummaryLandUseAnswer One land use's answer: can it be built, and by which codes. - `id` (string): The land use's id, as GET /v1/land-uses lists it. - `name` (string): The land use's name. - `verdict` (Verdict | null): The best of its ways to build it new; null when only alterations deliver it. - `ways` (SummaryWay[]): The codes that deliver it as new building work. - `alterations` (SummaryWay[]): The codes for altering an existing building for it. They don't count toward its verdict. ### CdcAnswer Complying development (CDC): what can be built on the property, by land use. - `ruleset` (string): The ruleset the answer came from. Answers can change when it does. - `landUses` (SummaryLandUseAnswer[]): Each land use asked about, or every one, with its answer. - `unknownCriticalFacts` (string[]): Facts PropCode couldn't determine that decide an answer. Supply them in factOverrides. - `reportUrl` (string): The property's CDC report in the PropCode app. ### Summary What can be built on one property, by pathway: complying development (cdc) for now. - `object` ("summary") - `property` (Property) - `evaluatedAt` (string): When the answer was worked out (ISO 8601). - `cdc` (CdcAnswer) - `usage` (object): What this answer cost. - `billed` (boolean): Whether this answer was billed: the first for the property this month, with a live key. - `period` (string): The billing month, YYYY-MM (UTC). - `sample` (true, optional): Present on a sample property, whose answers are free with any key. ### CodeGroup A group of codes. - `id` (string): The group's id: a stable slug, such as housing-code. - `name` (string) ### Code A complying development code the summary answers for. - `id` (string): The code's id. - `name` (string): The code's name. - `group` (CodeGroup) - `position` (number): Its place in the report's order. ### CodeList - `object` ("list") - `data` (Code[]) ### LandUse A land use, as Rapid Planning Reports and PropCode list them. - `id` (string): The id to send as landUse: stable, it never changes or names another land use. - `name` (string): The land use's name. - `category` (string): The kind of land use it is. - `popular` (boolean): One of the land uses most asked about. - `pathways` (object[]): The pathways that can deliver it, which the report answers it by; none when no pathway PropCode covers can. nsw-cdc covers NSW only. ### LandUseList - `object` ("list") - `data` (LandUse[]) ### Fact A fact you can override in POST /v1/summary. - `name` (string): The name to use in factOverrides. - `title` (string): What the fact is. - `type` ("numeric" | "text" | "boolean"): The kind of value it takes. - `unit` (string, optional): Numeric facts: the unit. - `allowedValues` (string[], optional): Text facts: the values it takes. - `question` (string, optional): The question the web report asks for it. ### FactList - `object` ("list") - `data` (Fact[]) ### PurchaseChoice Something your team can buy in PropCode to unlock properties with: Pro, for one property or as a Property Pack. - `name` (string): What it buys: "Just this property" or "Property Pack". - `price` (string, optional): Its price with GST, from PropCode's live catalog: e.g. A$82.50, or from A$27.50 a property for a pack. Left out when the catalog can't be read. - `url` (string): PropCode's checkout, opened on this choice. The person buys there; an agent never buys. ### Credits What your team has to unlock properties with, by credit type. - `object` ("credits") - `pro` (object): Pro credits from Property Packs, shared by your team. - `remaining` (integer): Pro credits left in your team's Property Packs. - `dailyAllowance` (object | null): Max Pro's unlocks for today, shared by your team's seats and spent before any pack; null without Max. - `cdc` (object): CDC report credits from a plan from before Core and Pro: each unlocks a property's CDC report (creditType cdc). - `remaining` (integer): CDC report credits left; 0 when your team has none. - `rpr` (object): Rapid Planning Report credits from a plan from before Core and Pro: each unlocks a property's Rapid Planning Report (creditType rpr). - `remaining` (integer): Rapid Planning Report credits left; 0 when your team has none. - `choices` (PurchaseChoice[], optional): When your team has nothing to unlock a property with, and you're its owner or an admin: what you can buy in PropCode, with links. - `note` (string, optional): When your team has nothing to unlock a property with: who buys credits, in a sentence or two to pass on to the person. ### Unlock A property your team has unlocked. - `object` ("unlock") - `property` (object) - `title` (string) - `jurisdiction` (string) - `product` ("pro" | "cdc" | "rpr"): What the team holds the property as: the creditType spent. - `spent` (boolean): Whether this request spent a credit: false when your team had already unlocked the property, and for a sample property's CDC report, which is free. - `paidWith` ("daily_allowance" | "pack", optional): What a Pro credit came from, when one was spent: Max's daily allowance or a pack. - `sample` (true, optional): Present on a sample property, whose Pro report is free with any key and needs no unlock, so nothing was spent. Its Rapid Planning Report is still paid: unlock it with creditType rpr, or in PropCode. - `shareUrl` (string, optional): On a Pro unlock in NSW, the Pro report's share link: anyone who has it can read the report, without signing in. Elsewhere, share the Rapid Planning Report's own. - `credits` (Credits) ### Conclusions What it allows on this lot, where it says. More limits may come. - `dwellings` (object, optional): How many dwellings it allows: min and max are the same unless it allows a range, such as 3 to 4. - `min` (integer) - `max` (integer) - `maxGfaM2` (number, optional): The largest gross floor area allowed, in m². - `maxHeightM` (number, optional): The greatest height, in m. - `minSubdivisionLotM2` (number | null, optional): The smallest lot a subdivision may make, in m²; null when the lot can't be subdivided this way. ### Way One way to deliver a land use under a pathway, and its answer: for nsw-cdc, a complying development code. - `id` (string): The way's id: for nsw-cdc, a code's id, as GET /v1/codes lists it. - `name` (string): The way's name. - `group` (string): The group it belongs to: for nsw-cdc, the code's group, such as Low Rise Housing Diversity Code. - `verdict` (Verdict) - `applies` (boolean | null): Whether it applies to this lot; null when a fact PropCode can’t determine decides it. - `conclusions` (Conclusions, optional) ### PathwayAnswer A land use's answer under one pathway. - `pathway` (string): The pathway's id, as pathways lists it, such as nsw-cdc. - `verdict` (Verdict | null): The land use's answer under this pathway: for nsw-cdc, the best of its ways to build it new; null when only alterations deliver it. - `ways` (Way[], optional): The ways to build it new under this pathway. With view=summary, only the eligible ones, and left out when none is. - `alterations` (Way[], optional): The ways to alter an existing building for it. They don't count toward its verdict. With view=summary, only the eligible ones, and left out when none is. ### ReportLandUse One land use's answers: can it be built, and by which pathway. - `id` (string): The land use's id, as GET /v1/land-uses lists it. - `name` (string): The land use's name. - `pathways` (PathwayAnswer[]): Its answer under each pathway the report answers it by. ### Rule One rule, and how the property fares against it. - `id` (string, optional): The rule's id, where it has one. - `ways` (string[]): The ids of the ways in this report that the rule belongs to. - `reference` (string): The clause it comes from, e.g. 3.9(1). - `instrument` (string, optional): The instrument the clause belongs to, by name. - `contentUrl` (string, optional): The clause's text (GET /v1/citations/…, or read_citation over MCP), at the instrument's version the rule was reviewed against, where PropCode has the clause in its Library. - `description` (string): What the rule requires, as the report words it. - `result` (string): met or not_met on this lot; unknown when a fact it reads isn't known. Read a value you don't know as unknown. - `message` (string, optional): Why, where the report says. - `certainty` (string, optional): How certain PropCode is of this answer, as the report marks it. Given on a property your team holds Pro on, and on the sample properties. Read a value you don't know as not_known. ### Pathway A planning pathway the report answers by. - `id` (string): Its id: the state, then the pathway, such as nsw-cdc. A new pathway adds a new id. - `name` (string): Its name, such as Complying development. - `rules` (Rule[], optional): The rules behind its answers, each once, as the report's Pro detail lists them. With landUse, only the rules of that land use's ways. ### KeyControl One of the property's key planning controls or constraints. - `name` (string): The fact's name, e.g. heightOfBuildingsEPI. - `title` (string): What it is, as PropCode words it. - `value` (string | number | boolean | null): Its value on this property, typed by the control: a number for a numeric control (its unit is in unit), text, or true or false. null when PropCode has none. - `unit` (string, optional): Its unit, e.g. m or m². ### Report One property's planning report: what can be built, by land use and pathway, and the rules behind each answer. - `object` ("report") - `property` (Property) - `evaluatedAt` (string): When the report was worked out (ISO 8601). - `pathways` (Pathway[]): The pathways the report answers by: nsw-cdc, complying development in NSW, for now. - `landUses` (ReportLandUse[]): Each land use the report answers, with its answer under each pathway; with landUse, just that one. A land use or a pathway left out is unanswered, never a no. - `keyControls` (KeyControl[], optional): The property's key planning controls and constraints, as the report reads them from PropCode's maps: its zone, height and floor space ratio limits, minimum lot size, lot area, and heritage, bush fire and flood control flags. - `unknownCriticalFacts` (string[]): Facts PropCode can’t determine that leave some answers with no_conclusion. - `reportUrl` (string): The report on PropCode, for your team. - `shareUrl` (string, optional): The report's share link, where your team holds Pro on the property: anyone who has it can read the report, without signing in. ### RprSection One section of the report. - `title` (string) - `html` (string): The section, as HTML as PropCode writes it. Each citation is its document's name and a [n] that citations explains. - `citations` (RprCitation[]) ### RprCitation A source the section cites. - `n` (integer): Its number, as [n] in the section. - `document` (string): The planning document, e.g. a local environmental plan. - `version` (string, optional): The document's version the report read, e.g. 2026-07-01. - `location` (string, optional): Where in it, by its headings, e.g. Part 4 › 4.3 Height of buildings. - `fileName` (string, optional): For a document published as PDFs, the name of the PDF the passage is in. Left out for legislation. - `page` (integer, optional): The page of that PDF the passage starts on, counting from 1, where the document records it. - `pdfUrl` (string, optional): PropCode's copy of the council's PDF, opened at the page (#page=…). It stops working if PropCode removes that copy. - `libraryUrl` (string, optional): The passage in PropCode's Library: the document at the version the report read, its file, and the place in it. - `contentUrl` (string, optional): The cited passage’s text, from the version the report read: GET it with your key (GET /v1/citations/…), or give it to read_citation over MCP. ### Citation The text a Rapid Planning Report citation points to, from the version the report read: the passage, or with includeParentSection, the section it is in. - `object` ("citation") - `document` (object) - `id` (string) - `name` (string) - `jurisdiction` (string) - `version` (string): The document's version, e.g. 2026-07-01. - `file` (integer): The document's file, by number, as the path names it. - `passageId` (integer): The passage, as the path names it (a text within an element reads as the element). - `location` (string, optional): Where the passage is in the document, by its headings, e.g. Part 4 › 4.3 Height of buildings. - `fileName` (string, optional): For a document published as PDFs, the name of the PDF the passage is in. Left out for legislation. - `page` (integer, optional): The page of that PDF the passage starts on, counting from 1, where the document records it. - `pdfUrl` (string, optional): PropCode's copy of the council's PDF, opened at the page (#page=…). It stops working if PropCode removes that copy. - `parentSection` (boolean): true: html and text are the passage's parent section, the passage in <mark>. false: they're the passage alone. - `html` (string): The text as HTML. - `text` (string): The same, plain: a line for each paragraph, list item or table row, with its number, e.g. (a). - `truncated` (true, optional): The passage runs past 10,000 characters, so its text stops there: libraryUrl has the rest. - `libraryUrl` (string): The passage in PropCode's Library, at this version. ### Rpr A property's Rapid Planning Report. - `object` ("rpr") - `id` (string): The report's id, for GET /v1/reports/rpr/{id}. - `property` (object) - `title` (string) - `jurisdiction` (string) - `address` (string) - `reportType` ("multiple_land_uses" | "single_land_use" | "subdivision") - `landUse` (string, optional): The land use's name, for single_land_use. - `status` ("generating" | "ready" | "incomplete" | "failed"): generating: in progress, usually 2–5 minutes; read it again. ready: generated, with its sections. incomplete: generated with sections missing (missing lists them), and the ones it has. failed: not generated (error says why). For incomplete or failed, POST /v1/reports/rpr again generates it again, once a day at most. - `alreadyGenerated` (boolean, optional): POST only. true: your team already had this report (one of each type, and one for each land use, per property), so this request generated nothing new. false: this request started generating it. - `createdAt` (string): When it was started (ISO 8601). - `reportUrl` (string): The report on PropCode. - `shareUrl` (string): The report's share link, as PropCode's Share copies it: anyone who has it can read the report, without signing in. - `outline` (object[], optional): Every section the report has, in order, once ready or incomplete. - `sections` (RprSection[], optional): The report, once ready or incomplete: every section, or the one ?section= asked for, or none with ?section=none. - `missing` (string[], optional): The sections that weren't generated, or came out cut short, when incomplete. - `error` (string, optional): What went wrong, when incomplete or failed. ### UnlockedProperty A property your team has unlocked, with its Rapid Planning Reports. - `object` ("unlocked_property") - `property` (object) - `title` (string) - `jurisdiction` (string) - `address` (string) - `products` ("pro" | "core" | "cdc" | "cdc_preview" | "rpr"[]): What your team holds it as: pro, core, or what a plan from before Core and Pro opened it with: cdc, cdc_preview (its CDC answers) or rpr. - `unlockedAt` (string): When your team last unlocked something new on it (ISO 8601): the latest of its products’ first unlocks. - `unlockedBy` (string): Who made that unlock: their email. - `shareUrl` (string, optional): Its Pro report's share link, where your team holds Pro on it in NSW: anyone who has it can read the report, without signing in. - `rprs` (object[]): Its Rapid Planning Reports, newest first. ### UnlockedPropertyList - `object` ("list") - `data` (UnlockedProperty[]) - `page` (number) - `limit` (number) - `total` (number): How many properties match, across every page. ### Feedback - `object` ("feedback") - `message` ("Feedback logged.") ### ErrorType The kind of error. Each has one HTTP status. One of: "invalid_request" | "authentication" | "billing" | "permission" | "not_found" | "ambiguous_address" | "invalid_fact_overrides" | "unsupported_jurisdiction" | "rate_limited" | "overloaded" | "internal". ### Error Every error has this shape, whatever its status. - `error` (object) - `type` (ErrorType) - `code` (string): What went wrong, e.g. spend_cap_reached. The errors page lists every code. - `message` (string): What went wrong, in a sentence. - `param` (string, optional): The parameter at fault, when there is one. - `requestId` (string): The request's id, also sent as Request-Id. Quote it if you contact us. - `docUrl` (string): This error code's entry on the errors page. - `candidates` (PropertyMatch[], optional): ambiguous_address: the properties the address could be. - `sandboxTitles` (string[], optional): sandbox_title_only: the titles a test key may ask about. - `choices` (PurchaseChoice[], optional): not_unlocked, no_credits and max_daily_limit, for your team's owner or an admin: what they can buy in PropCode, with links. A member gets none: the message says their team's admin buys. ## Error codes | Code | Status | What happened | What to do | |---|---|---|---| | `invalid_parameter` | 400 | A parameter is missing or not valid. param names it. | Correct that parameter. The reference lists each endpoint’s parameters. | | `invalid_request` | 400 | The request couldn’t be read, for example a body that isn’t JSON. | Send JSON with Content-Type: application/json, as the reference shows. | | `key_in_url` | 400 | An API key was sent in the URL, where logs and proxies can keep it. | Send keys only in the Authorization header. Revoke that key and make another. | | `invalid_address` | 400 | The address can’t be searched, for example without a street number. | Send a full street address, or search by title. | | `missing_api_key` | 401 | No API key was sent. | Send Authorization: Bearer pc_test_… or pc_live_… | | `invalid_api_key` | 401 | The key is unknown or has been revoked. | Check the key, or make a new one in Integrations & API › Keys. | | `authentication` | 401 | The request couldn’t be authenticated. message says why. | Send Authorization: Bearer with a key from Integrations & API › Keys. | | `billing_inactive` | 402 | A live key was used while its organisation’s API billing isn’t active. | Add a card, or update it, in Integrations & API › Billing & limits. | | `spend_cap_reached` | 402 | Checking a new property would pass the organisation’s monthly spend cap. Caps start at $0. | Raise the cap in Integrations & API › Billing & limits. Properties already checked this month still answer. | | `billing` | 402 | Billing stops this request. message says why. | Check Integrations & API › Billing & limits. | | `no_credits` | 402 | Unlocking the property needs a credit of the type you chose, and your team has none left. | GET /v1/credits lists what your team has. For Pro, an admin buys a single property or a Property Pack in PropCode, which can refill when it runs out: for them, choices lists both, with links. | | `not_eligible` | 400 | The property's CDC report can't be unlocked with a CDC report credit yet: PropCode can't determine a fact its verdicts depend on. | Unlock it in PropCode, where you can enter the fact, or unlock it with creditType pro. | | `max_daily_limit` | 402 | Max's daily allowance is used up for today, and your team has no pack credits to fall back on. | Wait until dailyAllowance.resetsAt in GET /v1/credits (midnight in Sydney), adjust your Max seats for more each day, or have an admin buy a Property Pack. | | `unlock_failed` | 500 | Your credit was spent, but unlocking the property didn’t finish. | Contact us with the requestId: we’ll finish the unlock or return the credit. | | `not_unlocked` | 403 | Your team hasn't unlocked this property as Pro. | POST /v1/unlocks with spendCredit: true unlocks it for one credit. With no credits, an admin of your team buys Pro in PropCode: for them, choices lists what they can buy, with links. The sample properties need no unlock for their Pro report. | | `key_owner_left` | 403 | Unlocks and reports act as the person who made the key, and they have left your team. | Make a new key in Integrations & API. | | `account_not_set_up` | 403 | You signed in to PropCode from an AI app, but your PropCode account isn't set up yet. | Open PropCode in your browser once, then connect again. | | `account_other_sign_in` | 403 | You signed in from an AI app with an email that already has a PropCode account, which signs in another way, such as Google. | Disconnect PropCode in the AI app and connect again, signing in the way the message says. | | `account_email_unverified` | 403 | You signed in from an AI app with an email address that isn't verified yet, and PropCode can't set up an account for this sign-in until it is. | Verify your email address with the link PropCode emailed you, then disconnect PropCode in the AI app and connect again. | | `account_setup_failed` | 503 | PropCode couldn't finish making your account at your first sign-in from an AI app. | Try again in a minute. | | `live_key_required` | 403 | Test keys can’t generate Rapid Planning Reports. | Make a live key in PropCode, under Integrations & API › Keys (anyone in your team can), unlock the property with it, then generate the report. | | `sandbox_title_only` | 403 | A test key asked about a property outside the sample properties. sandboxTitles lists them. | Use one of the sample properties, whose Pro reports are free, or a live key: anyone in your team can make one in PropCode, under Integrations & API › Keys. With a live key, searches and property lookups are free, and a property’s Pro report and Rapid Planning Report need an unlock, which spends one of your team’s credits. | | `permission` | 403 | This key can’t make that request. message says why. | Check which key you sent: test keys answer only for the sample properties. | | `title_not_found` | 404 | No property has that title reference. | Check the title, or search by address with GET /v1/properties. | | `address_not_found` | 404 | No property matches that address. | Check the address, or search by title. | | `not_found` | 404 | There’s no endpoint at that path. | Check the path against the reference: every endpoint starts /api/v1/. | | `citation_not_found` | 404 | The Library has no passage at that document, version, file and passage id. | Use a citation’s contentUrl as a report gives it. A report generated again may cite a newer version. | | `passage_too_broad` | 400 | That passage is the whole document (its top level), which this endpoint doesn’t return. | Ask for a passage a report cites, by its contentUrl, or read the document in the Library. | | `ambiguous_address` | 409 | The address matches more than one property. candidates lists them. | Ask again by title, using one of the candidates. | | `invalid_fact_overrides` | 422 | A fact in factOverrides is unknown, can’t be overridden (where the property is: its zone, LEP or maps), or its value is the wrong kind. | GET /v1/facts lists the facts and the values each takes. | | `unsupported_jurisdiction` | 422 | That state isn’t covered yet. | Use nsw, vic, act or tas. Complying development (the planning report, codes and summaries) is nsw only. | | `rate_limited` | 429 | The organisation’s keys sent more requests a second than its limit. | Wait the Retry-After seconds, then retry. Billing & limits shows the limits. | | `feedback_limit_reached` | 429 | The organisation has sent its most feedback for the day. | Retry when Retry-After says. It resets at midnight UTC. | | `too_many_concurrent` | 429 | The organisation already has its most properties being worked out at once: Pro reports, or summaries. | Wait for a request to finish, then retry. | | `daily_property_limit` | 429 | The organisation has checked, or looked up, its most new properties for the day. | Retry after midnight UTC (Retry-After says when). Properties already checked or looked up in the period still answer. | | `daily_search_limit` | 429 | Address searches are used up for the day: the organisation’s live keys have made their most, or the allowance all organisations share is spent. | Retry when Retry-After says, or search by point or ask by title, which aren’t limited this way. | | `daily_citation_limit` | 429 | The organisation’s live keys have read their most different cited passages for the day. | Retry when Retry-After says. Passages already read today still answer. Contact us if you need more. | | `amalgamation_limit` | 429 | New multi-lot titles are used up for the hour: the organisation’s live keys have made their most, or all organisations’ together have. | Retry when Retry-After says, or ask for titles that already exist. | | `sandbox_daily_limit` | 429 | The organisation’s test keys have used their requests for the day. | Retry after midnight UTC, or use a live key. | | `internal_error` | 500 | Something went wrong on our side. | Retry. If it keeps happening, contact us with the requestId. | | `overloaded` | 503 | PropCode is busy. | Retry in a few seconds. | | `engine_busy` | 503 | PropCode is busy. | Wait the Retry-After seconds, then retry. |