{
  "openapi": "3.1.0",
  "info": {
    "title": "Planning Signal API",
    "version": "1.0.0",
    "summary": "Read-only access to UK planning applications and decision-notice conditions.",
    "description": "Read-only REST API. Authenticate with `Authorization: Bearer ps_live_...`. Allowances are metered in records (distinct objects returned per period). Rate limit: 60 requests a minute per key. Full documentation: https://planningsignal.co.uk/developers/docs",
    "termsOfService": "https://planningsignal.co.uk/api-terms",
    "contact": {
      "name": "Planning Signal",
      "url": "https://planningsignal.co.uk/contact"
    }
  },
  "externalDocs": {
    "description": "Planning Signal API documentation",
    "url": "https://planningsignal.co.uk/developers/docs"
  },
  "servers": [
    {
      "url": "https://planningsignal.co.uk"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Applications",
      "description": "Planning applications. All plans."
    },
    {
      "name": "Conditions",
      "description": "Decision-notice conditions. API Pro, Leads and National."
    },
    {
      "name": "Account and reference",
      "description": "Free endpoints: councils and usage."
    },
    {
      "name": "Portfolio",
      "description": "Portfolio Monitor sites and matches. Portfolio Monitor customers."
    }
  ],
  "paths": {
    "/api/v1/applications": {
      "get": {
        "operationId": "listApplications",
        "summary": "List applications",
        "description": "Returns applications matching every filter you give (filters combine with AND). With no filters it lists every application.\n\nResults come in the order applications were added to Planning Signal, oldest first, and that order never changes, so paging with cursor is stable even while new applications arrive.\n\nEach application returned uses one record, unless you already received it this period.\n\nAccess: All plans, including the free demo.\n\nMetered: one record per application.",
        "tags": [
          "Applications"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/applications"
        },
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Case-insensitive text matched anywhere in the description or the address.",
            "schema": {
              "type": "string"
            },
            "example": "loft conversion"
          },
          {
            "name": "authority",
            "in": "query",
            "required": false,
            "description": "Council name, exactly as /api/v1/authorities lists it (case-insensitive). An unknown name returns an empty list, not an error.",
            "schema": {
              "type": "string"
            },
            "example": "Camden"
          },
          {
            "name": "postcode",
            "in": "query",
            "required": false,
            "description": "UK postcode at the centre of a radius search. Use with radius_m. 400 bad_request if the postcode cannot be located.",
            "schema": {
              "type": "string"
            },
            "example": "NW3 5NA"
          },
          {
            "name": "radius_m",
            "in": "query",
            "required": false,
            "description": "Radius around postcode in metres. Ignored without postcode. Values outside 100 to 20,000 are clamped to that range.",
            "schema": {
              "type": "integer",
              "minimum": 100,
              "maximum": 20000,
              "default": 1000
            },
            "example": 1500
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Only applications with start_date on or after this date. 400 bad_request unless YYYY-MM-DD.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "description": "Only applications with start_date on or before this date. 400 bad_request unless YYYY-MM-DD.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-30"
          },
          {
            "name": "decided_since",
            "in": "query",
            "required": false,
            "description": "Only applications with decided_date on or after this date (so only decided ones). 400 bad_request unless YYYY-MM-DD.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "description": "Only applications with this app_state (case-insensitive). Refusals are Rejected. Any other value matches nothing and returns an empty list.",
            "schema": {
              "type": "string",
              "enum": [
                "Undecided",
                "Permitted",
                "Conditions",
                "Rejected",
                "Withdrawn",
                "Appeal",
                "Unresolved",
                "Referred"
              ]
            },
            "example": "Undecided"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100. Defaults to 20. Missing, zero or non-numeric values use 20; values above 100 are treated as 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "next_cursor from the previous page, passed back exactly as returned (cur_...). 400 bad_request if altered, or if it came from another endpoint.",
            "schema": {
              "type": "string"
            },
            "example": "cur_5RbJ8uYc3XeN1qGh6tZkWo"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Application"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as cursor for the next page; null on the last page."
                    }
                  },
                  "required": [
                    "data",
                    "next_cursor"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "pa_3kTq9ZxV1bN7cR2mW8yLpA",
                      "reference": "2026/4417/P",
                      "authority": "Camden",
                      "address": "14 Fitzjohn's Avenue, London",
                      "postcode": "NW3 5NA",
                      "description": "Erection of a single storey rear extension and loft conversion with rear dormer.",
                      "app_type": "Full",
                      "app_state": "Undecided",
                      "decision": null,
                      "start_date": "2026-09-14",
                      "decided_date": null,
                      "lat": 51.5512,
                      "lng": -0.1765,
                      "url": "https://planning.example-council.gov.uk/application/2026-4417-P",
                      "docs_url": null,
                      "case_officer": "J. Patel",
                      "attribution": null
                    },
                    {
                      "id": "pa_0Hs4Ue8kLq2TzW7mB1xYcV",
                      "reference": "2026/4452/P",
                      "authority": "Camden",
                      "address": "Flat 3, 88 Belsize Park Gardens, London",
                      "postcode": "NW3 4NG",
                      "description": "Replacement of timber sash windows with double-glazed timber sash windows to front elevation.",
                      "app_type": "Householder",
                      "app_state": "Undecided",
                      "decision": null,
                      "start_date": "2026-09-16",
                      "decided_date": null,
                      "lat": 51.5481,
                      "lng": -0.1663,
                      "url": "https://planning.example-council.gov.uk/application/2026-4452-P",
                      "docs_url": null,
                      "case_officer": "R. Okafor",
                      "attribution": null
                    }
                  ],
                  "next_cursor": "cur_5RbJ8uYc3XeN1qGh6tZkWo"
                }
              }
            }
          },
          "400": {
            "description": "bad_request: Bad date, unlocatable postcode or altered cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_request": {
                    "summary": "Bad date, unlocatable postcode or altered cursor.",
                    "value": {
                      "error": "since must be a date in YYYY-MM-DD form.",
                      "code": "bad_request"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute. records_exhausted: No new record fits in your remaining allowance.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              },
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  },
                  "records_exhausted": {
                    "summary": "No new record fits in your remaining allowance.",
                    "value": {
                      "error": "Your record allowance for this month has been used.",
                      "code": "records_exhausted",
                      "plan": "starter",
                      "allowance": 2500,
                      "used": 2500,
                      "remaining": 0,
                      "resets": "2026-11-01T00:00:00.000Z",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/applications/{id}": {
      "get": {
        "operationId": "getApplication",
        "summary": "Get an application",
        "description": "Returns one application. Use it to refresh an application you stored earlier, for example to see whether it has been decided.\n\nUses one record, or none if you already received this application this period. If two records for the same application are ever combined, the old id keeps working and returns the surviving record, whose id may differ: store the id from the response.\n\nAccess: All plans, including the free demo.\n\nMetered: one record per application.",
        "tags": [
          "Applications"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/get-application"
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Application id (pa_...), as returned by a list response. 404 not_found if no application has this id.",
            "schema": {
              "type": "string",
              "pattern": "^pa_[0-9A-Za-z]{22}$"
            },
            "example": "pa_3kTq9ZxV1bN7cR2mW8yLpA"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Application"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "pa_3kTq9ZxV1bN7cR2mW8yLpA",
                    "reference": "2026/4417/P",
                    "authority": "Camden",
                    "address": "14 Fitzjohn's Avenue, London",
                    "postcode": "NW3 5NA",
                    "description": "Erection of a single storey rear extension and loft conversion with rear dormer.",
                    "app_type": "Full",
                    "app_state": "Undecided",
                    "decision": null,
                    "start_date": "2026-09-14",
                    "decided_date": null,
                    "lat": 51.5512,
                    "lng": -0.1765,
                    "url": "https://planning.example-council.gov.uk/application/2026-4417-P",
                    "docs_url": null,
                    "case_officer": "J. Patel",
                    "attribution": null,
                    "agent_name": "Sarah Holt",
                    "agent_company": "Holt Architecture Ltd",
                    "agent_address": "2 Example Mews, London NW1 8AA",
                    "agent_email": "studio@holt-architecture.example",
                    "agent_phone": "020 7946 0000",
                    "applicant_company": null,
                    "conditions_url": "/api/v1/applications/pa_3kTq9ZxV1bN7cR2mW8yLpA/conditions"
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "not_found: No application has this id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "summary": "No application has this id.",
                    "value": {
                      "error": "Application not found. Application ids look like pa_ followed by 22 letters and digits.",
                      "code": "not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute. records_exhausted: No new record fits in your remaining allowance.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              },
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  },
                  "records_exhausted": {
                    "summary": "No new record fits in your remaining allowance.",
                    "value": {
                      "error": "Your record allowance for this month has been used.",
                      "code": "records_exhausted",
                      "plan": "starter",
                      "allowance": 2500,
                      "used": 2500,
                      "remaining": 0,
                      "resets": "2026-11-01T00:00:00.000Z",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/applications/{id}/conditions": {
      "get": {
        "operationId": "getApplicationConditions",
        "summary": "Get an application's conditions",
        "description": "Returns the conditions read from the application's decision notice, in condition-number order, up to 100. The list is empty when the application is undecided or no conditions have been read for it yet.\n\nNot paginated: next_cursor is always null. Each condition returned uses one record, unless you already received it this period.\n\nAccess: API Pro, Leads and National.\n\nMetered: one record per condition.",
        "tags": [
          "Conditions"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/application-conditions"
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Application id (pa_...), as returned by a list response. 404 not_found if no application has this id.",
            "schema": {
              "type": "string",
              "pattern": "^pa_[0-9A-Za-z]{22}$"
            },
            "example": "pa_3kTq9ZxV1bN7cR2mW8yLpA"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Condition"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this endpoint is not paginated."
                    }
                  },
                  "required": [
                    "data",
                    "next_cursor"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "pc_7HfK2wQe9TzB4nLx0sVdMa",
                      "application_id": "pa_3kTq9ZxV1bN7cR2mW8yLpA",
                      "application_reference": "2026/4417/P",
                      "authority": "Camden",
                      "condition_number": "4",
                      "condition_text": "Prior to first occupation, a Travel Plan shall be submitted to and approved in writing by the local planning authority.",
                      "reason": "To promote sustainable modes of transport.",
                      "disciplines": [
                        "transport"
                      ],
                      "decided_date": "2026-09-22",
                      "source_pdf_url": "https://planning.example-council.gov.uk/documents/2026-4417-P-decision.pdf",
                      "created_at": "2026-09-23T06:12:44.000Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "plan_required: Your plan does not include conditions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "summary": "Your plan does not include conditions.",
                    "value": {
                      "error": "The conditions endpoints are part of API Pro.",
                      "code": "plan_required",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "not_found: No application has this id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "summary": "No application has this id.",
                    "value": {
                      "error": "Application not found. Application ids look like pa_ followed by 22 letters and digits.",
                      "code": "not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute. records_exhausted: No new record fits in your remaining allowance.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              },
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  },
                  "records_exhausted": {
                    "summary": "No new record fits in your remaining allowance.",
                    "value": {
                      "error": "Your record allowance for this month has been used.",
                      "code": "records_exhausted",
                      "plan": "starter",
                      "allowance": 2500,
                      "used": 2500,
                      "remaining": 0,
                      "resets": "2026-11-01T00:00:00.000Z",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/conditions": {
      "get": {
        "operationId": "listConditions",
        "summary": "List conditions",
        "description": "Returns conditions matching every filter you give, in the order they were added to Planning Signal, oldest first. Use since to collect new ones.\n\nEach condition returned uses one record, unless you already received it this period.\n\nAccess: API Pro, Leads and National.\n\nMetered: one record per condition.",
        "tags": [
          "Conditions"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/conditions"
        },
        "parameters": [
          {
            "name": "discipline",
            "in": "query",
            "required": false,
            "description": "Only conditions carrying this discipline tag (case-insensitive). Any other value matches nothing and returns an empty list.",
            "schema": {
              "type": "string",
              "enum": [
                "transport",
                "ecology",
                "arboriculture",
                "drainage",
                "contamination",
                "heritage",
                "acoustics",
                "air-quality",
                "energy",
                "landscape",
                "construction-management",
                "highways",
                "lighting",
                "waste",
                "fire-safety",
                "structural",
                "sustainability",
                "land-stability",
                "aviation",
                "materials"
              ]
            },
            "example": "transport"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Case-insensitive phrase matched anywhere in condition_text.",
            "schema": {
              "type": "string"
            },
            "example": "travel plan"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Only conditions added to Planning Signal on or after this date or timestamp (created_at). A date such as 2026-10-01 means midnight UTC. 400 bad_request if it is not a date or timestamp.",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "example": "2026-10-01"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100. Defaults to 20. Missing, zero or non-numeric values use 20; values above 100 are treated as 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "next_cursor from the previous page, passed back exactly as returned (cur_...). 400 bad_request if altered, or if it came from another endpoint.",
            "schema": {
              "type": "string"
            },
            "example": "cur_5RbJ8uYc3XeN1qGh6tZkWo"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Condition"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as cursor for the next page; null on the last page."
                    }
                  },
                  "required": [
                    "data",
                    "next_cursor"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "pc_7HfK2wQe9TzB4nLx0sVdMa",
                      "application_id": "pa_3kTq9ZxV1bN7cR2mW8yLpA",
                      "application_reference": "2026/4417/P",
                      "authority": "Camden",
                      "condition_number": "4",
                      "condition_text": "Prior to first occupation, a Travel Plan shall be submitted to and approved in writing by the local planning authority.",
                      "reason": "To promote sustainable modes of transport.",
                      "disciplines": [
                        "transport"
                      ],
                      "decided_date": "2026-09-22",
                      "source_pdf_url": "https://planning.example-council.gov.uk/documents/2026-4417-P-decision.pdf",
                      "created_at": "2026-09-23T06:12:44.000Z"
                    },
                    {
                      "id": "pc_2Bn8Qw5RtY1uI9oP3aSdFg",
                      "application_id": "pa_6Jd1Rz0QmX8bV4nT2kLwEs",
                      "application_reference": "26/02118/FUL",
                      "authority": "Leeds",
                      "condition_number": "11",
                      "condition_text": "No part of the development shall be occupied until a Travel Plan, including targets and a monitoring programme, has been submitted to and approved in writing by the Local Planning Authority.",
                      "reason": "In the interests of sustainable travel.",
                      "disciplines": [
                        "transport",
                        "highways"
                      ],
                      "decided_date": "2026-09-25",
                      "source_pdf_url": "https://publicaccess.example-council.gov.uk/documents/26-02118-FUL-decision.pdf",
                      "created_at": "2026-09-26T05:58:12.000Z"
                    }
                  ],
                  "next_cursor": "cur_5RbJ8uYc3XeN1qGh6tZkWo"
                }
              }
            }
          },
          "400": {
            "description": "bad_request: Bad since value or altered cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_request": {
                    "summary": "Bad since value or altered cursor.",
                    "value": {
                      "error": "since must be a date in YYYY-MM-DD form.",
                      "code": "bad_request"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "plan_required: Your plan does not include conditions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "summary": "Your plan does not include conditions.",
                    "value": {
                      "error": "The conditions endpoints are part of API Pro.",
                      "code": "plan_required",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute. records_exhausted: No new record fits in your remaining allowance.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              },
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  },
                  "records_exhausted": {
                    "summary": "No new record fits in your remaining allowance.",
                    "value": {
                      "error": "Your record allowance for this month has been used.",
                      "code": "records_exhausted",
                      "plan": "starter",
                      "allowance": 2500,
                      "used": 2500,
                      "remaining": 0,
                      "resets": "2026-11-01T00:00:00.000Z",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/authorities": {
      "get": {
        "operationId": "listAuthorities",
        "summary": "List councils",
        "description": "Returns every local planning authority we hold applications for, alphabetically, with how many we hold. Use the authority value as the authority filter on /api/v1/applications.\n\nFree: uses no records. The list is refreshed at most once an hour, so cache it on your side too.\n\nAccess: All plans, including the free demo.\n\nFree: uses no records.",
        "tags": [
          "Account and reference"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/authorities"
        },
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Authority"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "authority": "Camden",
                      "count": 15310
                    },
                    {
                      "authority": "Leeds",
                      "count": 21877
                    },
                    {
                      "authority": "Westminster",
                      "count": 18452
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Get usage",
        "description": "Returns the plan your key is on and how much of its allowance is left. The same figures come back in the X-Records-* headers of every response, so you only need this endpoint for a standalone check.\n\nFree: uses no records.\n\nAccess: All plans, including the free demo.\n\nFree: uses no records.",
        "tags": [
          "Account and reference"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/usage"
        },
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Usage"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "plan": "starter",
                    "allowance": 2500,
                    "used": 812,
                    "remaining": 1688,
                    "resets": "2026-11-01T00:00:00.000Z",
                    "contact_fields": false,
                    "conditions": false
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/portfolio/sites": {
      "get": {
        "operationId": "listPortfolioSites",
        "summary": "List portfolio sites",
        "description": "Returns the sites you uploaded to Portfolio Monitor, in the order you added them. Your own data, so it uses no records.\n\nAccess: Portfolio Monitor customers.\n\nFree: uses no records.",
        "tags": [
          "Portfolio"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/portfolio-sites"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 500. Defaults to 100. Missing, zero or non-numeric values use 100; values above 500 are treated as 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "next_cursor from the previous page, passed back exactly as returned (cur_...). 400 bad_request if altered, or if it came from another endpoint.",
            "schema": {
              "type": "string"
            },
            "example": "cur_5RbJ8uYc3XeN1qGh6tZkWo"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PortfolioSite"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as cursor for the next page; null on the last page."
                    }
                  },
                  "required": [
                    "data",
                    "next_cursor"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": 812,
                      "site_ref": "STORE-0147",
                      "name": "Hampstead High Street",
                      "address": "52 High Street, London",
                      "postcode": "NW3 1QH",
                      "lat": 51.5566,
                      "lng": -0.1779,
                      "uprn": null,
                      "region": "London North",
                      "tags": [
                        "retail"
                      ],
                      "radius_m": 250,
                      "watch_on_site": true,
                      "active": true,
                      "geocode_status": "ok",
                      "authority": "Camden",
                      "covered": true,
                      "created_at": "2026-10-01T09:30:00.000Z",
                      "updated_at": "2026-10-01T09:30:00.000Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "bad_request: Altered cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_request": {
                    "summary": "Altered cursor.",
                    "value": {
                      "error": "since must be a date in YYYY-MM-DD form.",
                      "code": "bad_request"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "plan_required: Portfolio Monitor is not active on the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "summary": "Portfolio Monitor is not active on the account.",
                    "value": {
                      "error": "The conditions endpoints are part of API Pro.",
                      "code": "plan_required",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/portfolio/matches": {
      "get": {
        "operationId": "listPortfolioMatches",
        "summary": "List portfolio matches",
        "description": "Returns matches between your sites and planning applications, first found on or after since, oldest first. Each match includes the full application.\n\nEach distinct application uses one record, unless you already received it this period.\n\nAccess: Portfolio Monitor customers.\n\nMetered: one record per application.",
        "tags": [
          "Portfolio"
        ],
        "externalDocs": {
          "url": "https://planningsignal.co.uk/developers/docs/portfolio-matches"
        },
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Only matches first found on or after this date or timestamp. Defaults to 7 days ago. 400 bad_request if it is not a date.",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "example": "2026-10-01"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100. Defaults to 100. Missing, zero or non-numeric values use 100; values above 100 are treated as 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "next_cursor from the previous page, passed back exactly as returned (cur_...). 400 bad_request if altered, or if it came from another endpoint.",
            "schema": {
              "type": "string"
            },
            "example": "cur_5RbJ8uYc3XeN1qGh6tZkWo"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PortfolioMatch"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as cursor for the next page; null on the last page."
                    }
                  },
                  "required": [
                    "data",
                    "next_cursor"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": 40213,
                      "site_id": 812,
                      "site_ref": "STORE-0147",
                      "match_kind": "nearby",
                      "distance_m": 180,
                      "first_seen_at": "2026-10-02T05:41:09.000Z",
                      "application": {
                        "id": "pa_3kTq9ZxV1bN7cR2mW8yLpA",
                        "reference": "2026/4417/P",
                        "authority": "Camden",
                        "address": "14 Fitzjohn's Avenue, London",
                        "postcode": "NW3 5NA",
                        "description": "Erection of a single storey rear extension and loft conversion with rear dormer.",
                        "app_type": "Full",
                        "app_state": "Undecided",
                        "decision": null,
                        "start_date": "2026-09-14",
                        "decided_date": null,
                        "lat": 51.5512,
                        "lng": -0.1765,
                        "url": "https://planning.example-council.gov.uk/application/2026-4417-P",
                        "docs_url": null,
                        "case_officer": "J. Patel",
                        "attribution": null
                      }
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "bad_request: Bad since value or altered cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_request": {
                    "summary": "Bad since value or altered cursor.",
                    "value": {
                      "error": "since must be a date in YYYY-MM-DD form.",
                      "code": "bad_request"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Missing, invalid or revoked key.",
                    "value": {
                      "error": "Missing or invalid Authorization header. Use: Authorization: Bearer ps_live_...",
                      "code": "unauthorized"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "plan_required: Portfolio Monitor is not active on the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "summary": "Portfolio Monitor is not active on the account.",
                    "value": {
                      "error": "The conditions endpoints are part of API Pro.",
                      "code": "plan_required",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Over 60 requests a minute. records_exhausted: No new record fits in your remaining allowance.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (rate_limited only)."
              },
              "X-Api-Plan": {
                "$ref": "#/components/headers/X-Api-Plan"
              },
              "X-Records-Limit": {
                "$ref": "#/components/headers/X-Records-Limit"
              },
              "X-Records-Used": {
                "$ref": "#/components/headers/X-Records-Used"
              },
              "X-Records-Remaining": {
                "$ref": "#/components/headers/X-Records-Remaining"
              },
              "X-Records-Charged": {
                "$ref": "#/components/headers/X-Records-Charged"
              },
              "X-Records-Reset": {
                "$ref": "#/components/headers/X-Records-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Over 60 requests a minute.",
                    "value": {
                      "error": "Rate limit exceeded (60 requests/min). Please slow down.",
                      "code": "rate_limited",
                      "retry_after_seconds": 60
                    }
                  },
                  "records_exhausted": {
                    "summary": "No new record fits in your remaining allowance.",
                    "value": {
                      "error": "Your record allowance for this month has been used.",
                      "code": "records_exhausted",
                      "plan": "starter",
                      "allowance": 2500,
                      "used": 2500,
                      "remaining": 0,
                      "resets": "2026-11-01T00:00:00.000Z",
                      "upgrade_url": "https://planningsignal.co.uk/developers#pricing"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "server_error: Query failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "server_error": {
                    "summary": "Query failed.",
                    "value": {
                      "error": "Query failed.",
                      "code": "server_error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ps_live_ followed by 32 letters and digits",
        "description": "Create a key at https://planningsignal.co.uk/developers#keys."
      }
    },
    "headers": {
      "X-Api-Plan": {
        "description": "The plan this request was served under: demo, starter, pro, leads, portfolio or custom.",
        "schema": {
          "type": "string"
        },
        "example": "starter"
      },
      "X-Records-Limit": {
        "description": "Your allowance for the current period.",
        "schema": {
          "type": "string"
        },
        "example": "2500"
      },
      "X-Records-Used": {
        "description": "Records used in the current period, including this response.",
        "schema": {
          "type": "string"
        },
        "example": "812"
      },
      "X-Records-Remaining": {
        "description": "Records left in the current period.",
        "schema": {
          "type": "string"
        },
        "example": "1688"
      },
      "X-Records-Charged": {
        "description": "New records this response used. 0 when everything in it was already returned this period, or the endpoint is free.",
        "schema": {
          "type": "string"
        },
        "example": "20"
      },
      "X-Records-Reset": {
        "description": "When the allowance resets (ISO 8601, UTC), or never on the demo.",
        "schema": {
          "type": "string"
        },
        "example": "2026-11-01T00:00:00.000Z"
      },
      "X-RateLimit-Limit": {
        "description": "Requests allowed per minute for this key.",
        "schema": {
          "type": "string"
        },
        "example": "60"
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current minute.",
        "schema": {
          "type": "string"
        },
        "example": "57"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message."
          },
          "code": {
            "type": "string",
            "enum": [
              "bad_request",
              "unauthorized",
              "plan_required",
              "suspended",
              "not_found",
              "rate_limited",
              "records_exhausted",
              "server_error"
            ]
          },
          "upgrade_url": {
            "type": "string"
          },
          "retry_after_seconds": {
            "type": "integer"
          },
          "plan": {
            "type": "string"
          },
          "allowance": {
            "type": [
              "integer",
              "null"
            ]
          },
          "used": {
            "type": "integer"
          },
          "remaining": {
            "type": "integer"
          },
          "resets": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "error",
          "code"
        ]
      },
      "Application": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable, opaque id for the application: pa_ followed by 22 letters and digits. Store it to fetch the application or its conditions later. Do not parse it.",
            "examples": [
              "pa_3kTq9ZxV1bN7cR2mW8yLpA"
            ]
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "The council's own reference. Unique only within one council.",
            "examples": [
              "2026/4417/P"
            ]
          },
          "authority": {
            "type": [
              "string",
              "null"
            ],
            "description": "Local planning authority name, exactly as /api/v1/authorities lists it and as the authority filter expects it.",
            "examples": [
              "Camden"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Site address as the council published it.",
            "examples": [
              "14 Fitzjohn's Avenue, London"
            ]
          },
          "postcode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Site postcode, where known.",
            "examples": [
              "NW3 5NA"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "The proposal, in the council's wording.",
            "examples": [
              "Erection of a single storey rear extension and loft conversion with rear dormer."
            ]
          },
          "app_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Application type in the council's own terms, for example Full, Householder, Outline, Conditions or Trees. Wording varies between councils.",
            "examples": [
              "Full"
            ]
          },
          "app_state": {
            "type": [
              "string",
              "null"
            ],
            "description": "Status, one of: Undecided, Permitted, Conditions, Rejected, Withdrawn, Appeal, Unresolved, Referred. See the state values table.",
            "examples": [
              "Undecided"
            ]
          },
          "decision": {
            "type": [
              "string",
              "null"
            ],
            "description": "The council's decision wording once decided, otherwise null."
          },
          "start_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Date the council received or validated the application (YYYY-MM-DD). The since and until filters use this date.",
            "examples": [
              "2026-09-14"
            ]
          },
          "decided_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Decision date (YYYY-MM-DD), null until decided. The decided_since filter uses this date."
          },
          "lat": {
            "type": [
              "number",
              "null"
            ],
            "description": "Latitude (WGS84) of the site, where known.",
            "examples": [
              51.5512
            ]
          },
          "lng": {
            "type": [
              "number",
              "null"
            ],
            "description": "Longitude (WGS84) of the site, where known.",
            "examples": [
              -0.1765
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The application's page on the council's own planning register, so you and your users can check the official record. Null when we hold no council page for it.",
            "examples": [
              "https://planning.example-council.gov.uk/application/2026-4417-P"
            ]
          },
          "docs_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The council's documents page for the application, where it has a separate one."
          },
          "case_officer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Case officer name, where the council publishes it.",
            "examples": [
              "J. Patel"
            ]
          },
          "attribution": {
            "type": [
              "string",
              "null"
            ],
            "description": "A licence notice the record's source requires, otherwise null. Records published under the Open Government Licence carry \"Contains public sector information licensed under the Open Government Licence v3.0.\" Keep it with the record and show it wherever you show the record publicly."
          },
          "agent_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent (the architect or consultant acting for the applicant), where published. API Pro, Leads and National only.",
            "examples": [
              "Sarah Holt"
            ]
          },
          "agent_company": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent's company, where published. API Pro, Leads and National only.",
            "examples": [
              "Holt Architecture Ltd"
            ]
          },
          "agent_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent's business address, where published. API Pro, Leads and National only.",
            "examples": [
              "2 Example Mews, London NW1 8AA"
            ]
          },
          "agent_email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent's business email, where published. API Pro, Leads and National only.",
            "examples": [
              "studio@holt-architecture.example"
            ]
          },
          "agent_phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent's business phone, where published. API Pro, Leads and National only.",
            "examples": [
              "020 7946 0000"
            ]
          },
          "applicant_company": {
            "type": [
              "string",
              "null"
            ],
            "description": "Applicant, only when the applicant is a company. Private individuals' names are never returned. API Pro, Leads and National only."
          },
          "conditions_url": {
            "type": "string",
            "description": "Relative path of this application's conditions endpoint. Present only on plans that include conditions. Plans with conditions only.",
            "examples": [
              "/api/v1/applications/pa_3kTq9ZxV1bN7cR2mW8yLpA/conditions"
            ]
          }
        },
        "required": [
          "id",
          "reference",
          "authority",
          "address",
          "postcode",
          "description",
          "app_type",
          "app_state",
          "decision",
          "start_date",
          "decided_date",
          "lat",
          "lng",
          "url",
          "docs_url",
          "case_officer",
          "attribution"
        ]
      },
      "Condition": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque condition id: pc_ followed by 22 letters and digits.",
            "examples": [
              "pc_7HfK2wQe9TzB4nLx0sVdMa"
            ]
          },
          "application_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The parent application's id (pa_...), or null when the decision notice could not be tied to an application record.",
            "examples": [
              "pa_3kTq9ZxV1bN7cR2mW8yLpA"
            ]
          },
          "application_reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "The council's reference for the parent application.",
            "examples": [
              "2026/4417/P"
            ]
          },
          "authority": {
            "type": [
              "string",
              "null"
            ],
            "description": "Local planning authority name.",
            "examples": [
              "Camden"
            ]
          },
          "condition_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "The condition's number on the decision notice, as text (councils number them differently).",
            "examples": [
              "4"
            ]
          },
          "condition_text": {
            "type": [
              "string",
              "null"
            ],
            "description": "The condition, in the council's wording.",
            "examples": [
              "Prior to first occupation, a Travel Plan shall be submitted to and approved in writing by the local planning authority."
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "The council's stated reason for the condition.",
            "examples": [
              "To promote sustainable modes of transport."
            ]
          },
          "disciplines": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Discipline tags, possibly empty. Values: transport, ecology, arboriculture, drainage, contamination, heritage, acoustics, air-quality, energy, landscape, construction-management, highways, lighting, waste, fire-safety, structural, sustainability, land-stability, aviation, materials.",
            "examples": [
              [
                "transport"
              ]
            ]
          },
          "decided_date": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decision date of the notice (usually YYYY-MM-DD).",
            "examples": [
              "2026-09-22"
            ]
          },
          "source_pdf_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The council's own decision-notice document.",
            "examples": [
              "https://planning.example-council.gov.uk/documents/2026-4417-P-decision.pdf"
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the condition was added to Planning Signal. The since filter on /api/v1/conditions uses this.",
            "examples": [
              "2026-09-23T06:12:44.000Z"
            ]
          }
        },
        "required": [
          "id",
          "application_id",
          "application_reference",
          "authority",
          "condition_number",
          "condition_text",
          "reason",
          "disciplines",
          "decided_date",
          "source_pdf_url",
          "created_at"
        ]
      },
      "Usage": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "description": "demo, starter, pro, leads, portfolio or custom (National and other agreed plans).",
            "examples": [
              "starter"
            ]
          },
          "allowance": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Records in the current period (the whole demo allowance on the demo).",
            "examples": [
              2500
            ]
          },
          "used": {
            "type": "integer",
            "description": "Records used in the current period.",
            "examples": [
              812
            ]
          },
          "remaining": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Records left in the current period.",
            "examples": [
              1688
            ]
          },
          "resets": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the allowance resets (00:00 UTC on the 1st of next month). null on the demo, which never resets.",
            "examples": [
              "2026-11-01T00:00:00.000Z"
            ]
          },
          "contact_fields": {
            "type": "boolean",
            "description": "Whether your plan includes the agent and applicant-company fields.",
            "examples": [
              false
            ]
          },
          "conditions": {
            "type": "boolean",
            "description": "Whether your plan includes the conditions endpoints.",
            "examples": [
              false
            ]
          }
        },
        "required": [
          "plan",
          "allowance",
          "used",
          "remaining",
          "resets",
          "contact_fields",
          "conditions"
        ]
      },
      "Authority": {
        "type": "object",
        "properties": {
          "authority": {
            "type": "string",
            "description": "Council name, exactly as the authority filter expects it.",
            "examples": [
              "Camden"
            ]
          },
          "count": {
            "type": "integer",
            "description": "How many applications we hold for the council.",
            "examples": [
              15310
            ]
          }
        },
        "required": [
          "authority",
          "count"
        ]
      },
      "PortfolioSite": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Your site's id in Planning Signal.",
            "examples": [
              812
            ]
          },
          "site_ref": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own reference for the site, from your upload.",
            "examples": [
              "STORE-0147"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Site label.",
            "examples": [
              "Hampstead High Street"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Site address.",
            "examples": [
              "52 High Street, London"
            ]
          },
          "postcode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Site postcode.",
            "examples": [
              "NW3 1QH"
            ]
          },
          "lat": {
            "type": [
              "number",
              "null"
            ],
            "description": "Latitude (WGS84).",
            "examples": [
              51.5566
            ]
          },
          "lng": {
            "type": [
              "number",
              "null"
            ],
            "description": "Longitude (WGS84).",
            "examples": [
              -0.1779
            ]
          },
          "uprn": {
            "type": [
              "string",
              "null"
            ],
            "description": "UPRN, if you supplied one."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your grouping label.",
            "examples": [
              "London North"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Your tags (possibly empty).",
            "examples": [
              [
                "retail"
              ]
            ]
          },
          "radius_m": {
            "type": "integer",
            "description": "How far around the site is watched, in metres (50 to 2000).",
            "examples": [
              250
            ]
          },
          "watch_on_site": {
            "type": "boolean",
            "description": "Whether on-site matches are reported.",
            "examples": [
              true
            ]
          },
          "active": {
            "type": "boolean",
            "description": "Whether the site is being watched.",
            "examples": [
              true
            ]
          },
          "geocode_status": {
            "type": "string",
            "description": "ok (located exactly), approx (placed approximately) or failed.",
            "examples": [
              "ok"
            ]
          },
          "authority": {
            "type": [
              "string",
              "null"
            ],
            "description": "The council the site is in, where known.",
            "examples": [
              "Camden"
            ]
          },
          "covered": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether that council is covered; null if not yet checked.",
            "examples": [
              true
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the site was added.",
            "examples": [
              "2026-10-01T09:30:00.000Z"
            ]
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the site last changed.",
            "examples": [
              "2026-10-01T09:30:00.000Z"
            ]
          }
        },
        "required": [
          "id",
          "site_ref",
          "name",
          "address",
          "postcode",
          "lat",
          "lng",
          "uprn",
          "region",
          "tags",
          "radius_m",
          "watch_on_site",
          "active",
          "geocode_status",
          "authority",
          "covered",
          "created_at",
          "updated_at"
        ]
      },
      "PortfolioMatch": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Match id.",
            "examples": [
              40213
            ]
          },
          "site_id": {
            "type": "integer",
            "description": "The site's id (see /api/v1/portfolio/sites).",
            "examples": [
              812
            ]
          },
          "site_ref": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your reference for the site.",
            "examples": [
              "STORE-0147"
            ]
          },
          "match_kind": {
            "type": "string",
            "description": "on_site (the site is inside the application's red-line boundary) or nearby (within the site's radius).",
            "examples": [
              "nearby"
            ]
          },
          "distance_m": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Distance from the site in metres (nearby matches).",
            "examples": [
              180
            ]
          },
          "first_seen_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the match was first found. The since filter uses this.",
            "examples": [
              "2026-10-02T05:41:09.000Z"
            ]
          },
          "application": {
            "$ref": "#/components/schemas/Application"
          }
        },
        "required": [
          "id",
          "site_id",
          "site_ref",
          "match_kind",
          "distance_m",
          "first_seen_at",
          "application"
        ]
      }
    }
  }
}