{"openapi":"3.1.0","info":{"title":"Spatibase Content Delivery API","version":"1.0.0","description":"Read-only API for the published content of a Spatibase project: entries with their locations, types, groups, GeoJSON and vector tiles. Generated from the server's response schemas, which the test suite checks against every response.\n\n**Keys.** A public project needs no key. A private project needs a read key (`sb_live_…`) of that project on every request: `Authorization: Bearer <key>`, `X-API-Key: <key>` or `?api_key=<key>` (for map libraries that can't send headers; prefer a header). Public projects accept and ignore keys. Edit keys (`sb_edit_…`) work too, but only in headers.\n\n**Caching.** Every 200/204/304 has an ETag; send it back as If-None-Match to get 304 when nothing changed. Public: `Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=86400`; private: `private, max-age=60` with `Vary: Authorization, X-API-Key`. Errors are `no-store`.\n\n**Fields.** Entry fields are keyed by each project's own element apiKeys, so this generic document types them loosely (FieldValue); the value shape per kind is in the FieldValue description and the named schemas. For typed fields, use the project's own document: `/delivery/v1/projects/{projectId}/openapi.json`.","contact":{"name":"Spatibase","url":"https://spatibase.com/docs/"},"license":{"name":"MIT (this API description only)","identifier":"MIT"},"termsOfService":"https://spatibase.com/legal/terms/"},"servers":[{"url":"https://api.spatibase.com","description":"Spatibase API"}],"security":[{},{"bearerAuth":[]},{"apiKeyHeader":[]},{"apiKeyQuery":[]}],"tags":[{"name":"Project","description":"The project and its content model"},{"name":"Groups","description":"Curated lists of entries"},{"name":"Entries","description":"Published entries, as JSON or GeoJSON"},{"name":"Tiles","description":"Vector tiles of the main locations"},{"name":"Files","description":"Files and images of entries (outside /delivery/v1, no key)"},{"name":"Webhooks","description":"Signed messages Spatibase sends to your server when published content changes"},{"name":"Meta","description":"About this API"}],"paths":{"/delivery/v1/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This OpenAPI document","description":"The OpenAPI 3.1 description of the Content Delivery API. Needs no API key, also for private projects.","tags":["Meta"],"security":[{}],"parameters":[{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"304":{"$ref":"#/components/responses/NotModified"}}}},"/delivery/v1/projects/{projectId}/openapi.json":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"getProjectOpenApi","summary":"This project's typed OpenAPI document","description":"The OpenAPI 3.1 description of this project's Delivery API, with one schema per Type, enums of its types, groups and choice options, and a named parameter per filter. Regenerate clients after changing the content model.","tags":["Meta"],"parameters":[{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"getProject","summary":"Get the project with its types, groups and feature types","tags":["Project"],"parameters":[{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/types":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"listTypes","summary":"List the enabled types (tree order) with their effective elements","tags":["Project"],"parameters":[{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Type"}}}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/feature-types":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"listFeatureTypes","summary":"List the geo feature types","tags":["Project"],"parameters":[{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FeatureType"}}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/groups":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"listGroups","summary":"List the groups with their number of published entries","tags":["Groups"],"parameters":[{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Group"}}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/groups/{groupKey}":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"getGroup","summary":"Get a group with its published entries in group order","tags":["Groups"],"parameters":[{"$ref":"#/components/parameters/groupKey"},{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/include"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupWithEntries"}}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/entries":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"listEntries","summary":"List and search published entries","tags":["Entries"],"parameters":[{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/type"},{"$ref":"#/components/parameters/subtypes"},{"$ref":"#/components/parameters/group"},{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/filter"},{"$ref":"#/components/parameters/bbox"},{"$ref":"#/components/parameters/near"},{"$ref":"#/components/parameters/radius"},{"$ref":"#/components/parameters/nearest"},{"$ref":"#/components/parameters/within"},{"$ref":"#/components/parameters/withinEntry"},{"$ref":"#/components/parameters/withinField"},{"$ref":"#/components/parameters/contains"},{"$ref":"#/components/parameters/containsField"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/include"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryList"}}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/entries.geojson":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"listEntriesGeojson","summary":"Published entries with a main location as a GeoJSON FeatureCollection","description":"The same filters as listEntries. Entries without a main location are left out.","tags":["Entries"],"parameters":[{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/type"},{"$ref":"#/components/parameters/subtypes"},{"$ref":"#/components/parameters/group"},{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/filter"},{"$ref":"#/components/parameters/bbox"},{"$ref":"#/components/parameters/near"},{"$ref":"#/components/parameters/radius"},{"$ref":"#/components/parameters/nearest"},{"$ref":"#/components/parameters/within"},{"$ref":"#/components/parameters/withinEntry"},{"$ref":"#/components/parameters/withinField"},{"$ref":"#/components/parameters/contains"},{"$ref":"#/components/parameters/containsField"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/geojsonLimit"},{"$ref":"#/components/parameters/include"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/geo+json":{"schema":{"$ref":"#/components/schemas/EntryFeatureCollection"}}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/entries.index":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"listEntriesIndex","summary":"A compact index of published entries: id, type, title and bounding box","description":"The same filters and sort as listEntries, without fields and geometries, and up to 5000 per page: all entries of a large project in a few requests, e.g. for a list or a search box. Fetch an entry's details from getEntry.","tags":["Entries"],"parameters":[{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/type"},{"$ref":"#/components/parameters/subtypes"},{"$ref":"#/components/parameters/group"},{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/filter"},{"$ref":"#/components/parameters/bbox"},{"$ref":"#/components/parameters/near"},{"$ref":"#/components/parameters/radius"},{"$ref":"#/components/parameters/nearest"},{"$ref":"#/components/parameters/within"},{"$ref":"#/components/parameters/withinEntry"},{"$ref":"#/components/parameters/withinField"},{"$ref":"#/components/parameters/contains"},{"$ref":"#/components/parameters/containsField"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/geojsonLimit"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryIndex"}}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/entries/{entryId}":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"getEntry","summary":"Get one published entry","tags":["Entries"],"parameters":[{"$ref":"#/components/parameters/entryId"},{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/include"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryResponse"}}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/tiles.json":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"getTileJson","summary":"TileJSON for the vector tiles","tags":["Tiles"],"parameters":[{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TileJson"}}}},"304":{"$ref":"#/components/responses/NotModified"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/delivery/v1/projects/{projectId}/tiles/{z}/{x}/{y}.pbf":{"parameters":[{"$ref":"#/components/parameters/projectId"}],"get":{"operationId":"getTile","summary":"A Mapbox Vector Tile of the published main locations","description":"One layer, `entries`, with the properties id, type, title, color and the scalar element values (strings cut at 255 characters, multiple choices joined with \", \"). Empty tile → 204.","tags":["Tiles"],"parameters":[{"$ref":"#/components/parameters/z"},{"$ref":"#/components/parameters/x"},{"$ref":"#/components/parameters/y"},{"$ref":"#/components/parameters/type"},{"$ref":"#/components/parameters/subtypes"},{"$ref":"#/components/parameters/group"},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/vnd.mapbox-vector-tile":{"schema":{"type":"string","format":"binary"}}}},"204":{"description":"Empty tile (nothing in it)","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}}},"304":{"$ref":"#/components/responses/NotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/assets/{assetId}/{filename}":{"get":{"operationId":"getAssetFile","summary":"A file or image, optionally resized or converted","description":"Every Asset.url in Delivery output (images and files elements, links in markdown) points here. **No API key, also for private projects:** an `<img>` can't send one, so files stay reachable by their unguessable URL. Without parameters you get the original. w, h, fit, fmt and q apply to images (JPEG, PNG, GIF, WebP, AVIF, SVG) and are ignored for other files. Without fmt a resized image keeps its format; SVG is served as-is unless fmt is given (then it is rasterised, also above its nominal size). Variants are cached, and every response is immutable: an asset id never gets other content.","tags":["Files"],"security":[{}],"parameters":[{"name":"assetId","in":"path","required":true,"description":"The asset's id (Asset.id)","schema":{"type":"string","format":"uuid"}},{"name":"filename","in":"path","required":true,"description":"Cosmetic: the file is found by its id (Delivery URLs use the file's name)","schema":{"type":"string"}},{"name":"w","in":"query","description":"Width in px, 1–4000. Snaps up to the next step of a fixed size ladder (16, 32, 48, 64, 96, 128, 160, 200, 256, 320, 400, 480, 512, 640, 800, 960, 1024, 1280, 1600, 1920, 2048, 2560, 3200, 3840; larger values → 3840). A raster image is never enlarged beyond its original size; with w, h and cover/contain the box keeps its aspect ratio while it shrinks to fit","schema":{"type":"integer","minimum":1,"maximum":4000},"example":800},{"name":"h","in":"query","description":"Height in px, 1–4000. Snaps up to the next step of a fixed size ladder (16, 32, 48, 64, 96, 128, 160, 200, 256, 320, 400, 480, 512, 640, 800, 960, 1024, 1280, 1600, 1920, 2048, 2560, 3200, 3840; larger values → 3840). A raster image is never enlarged beyond its original size; with w, h and cover/contain the box keeps its aspect ratio while it shrinks to fit","schema":{"type":"integer","minimum":1,"maximum":4000},"example":600},{"name":"fit","in":"query","description":"With w and h: cover (default) crops to fill the box, contain fits inside it with transparent bars (white for jpeg), inside fits without bars. With one side: inside. fit alone changes nothing","schema":{"type":"string","enum":["cover","contain","inside"]}},{"name":"fmt","in":"query","description":"Output format (case-insensitive; jpg = jpeg). The response's Content-Type and file name follow it","schema":{"type":"string","enum":["webp","jpeg","jpg","png","avif"]}},{"name":"q","in":"query","description":"Quality for webp, jpeg and avif (default 80), rounded to the nearest 10 (at least 10); png ignores it. q alone re-encodes a JPEG, WebP or AVIF in its own format","schema":{"type":"integer","minimum":1,"maximum":100,"default":80},"example":80},{"$ref":"#/components/parameters/ifNoneMatch"}],"responses":{"200":{"description":"The file, with its own Content-Type (with fmt: the chosen format)","headers":{"Cache-Control":{"description":"public, max-age=31536000, immutable","schema":{"type":"string"}},"ETag":{"description":"Send it as If-None-Match to get 304","schema":{"type":"string"}},"Content-Disposition":{"description":"inline; filename=\"<name>\"; filename*=UTF-8''<name> (with fmt, the extension follows the format)","schema":{"type":"string"}},"X-Variant-Cache":{"description":"HIT or MISS: whether a resized or converted image came from the variant cache","schema":{"type":"string","enum":["HIT","MISS"]}},"Content-Security-Policy":{"description":"A sandbox policy for types a browser would run (SVG, HTML, XML, JavaScript)","schema":{"type":"string"}}},"content":{"*/*":{"schema":{"type":"string","format":"binary"}}}},"206":{"description":"A byte range (Range request) of a file or a cached variant","content":{"*/*":{"schema":{"type":"string","format":"binary"}}}},"304":{"description":"Not modified: the If-None-Match ETag is still current (no body). The ETag of a variant's first, freshly rendered response differs from the cached copy's, so revalidating that one gets a 200 once"},"400":{"description":"An image parameter is invalid or given twice (bad_request); never for other files","headers":{"Cache-Control":{"description":"no-store","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such asset, or its file is missing (not_found)","headers":{"Cache-Control":{"description":"no-store","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"entries.published":{"post":{"operationId":"webhookEntriesPublished","summary":"Entries went live or changed: a project publish, or going back to a version","description":"Sent to the project's webhook address (API → Webhooks in the app; one per project). Body: ids only, read the content from the Delivery API. Headers also include Content-Type: application/json and User-Agent: Spatibase-Webhooks/1.","tags":["Webhooks"],"security":[{}],"parameters":[{"name":"Spatibase-Signature","in":"header","required":true,"description":"t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">, keyed with the project's signing secret (the whole whsec_… text). To verify, compute it over the body exactly as received (before parsing JSON), compare in constant time, and refuse a t older than a few minutes so a captured message can't be replayed. Rotating the secret signs new messages with the new one at once","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"Spatibase-Event","in":"header","required":true,"description":"The event, as in the body's type","schema":{"type":"string","enum":["entries.published"]}},{"name":"Spatibase-Delivery","in":"header","required":true,"description":"The delivery id, as in the body's id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEntriesPublished"}}}},"responses":{"2XX":{"description":"Received. Answer within 5 seconds. Anything else (3xx too: redirects aren't followed) is a failure: events are tried up to 3 times (after 1 s and 10 s, same id), the test ping once"}}}},"entries.unpublished":{"post":{"operationId":"webhookEntriesUnpublished","summary":"Entries came off the live API: a project publish, or going back to a version","description":"Sent to the project's webhook address (API → Webhooks in the app; one per project). Body: ids only, read the content from the Delivery API. Headers also include Content-Type: application/json and User-Agent: Spatibase-Webhooks/1.","tags":["Webhooks"],"security":[{}],"parameters":[{"name":"Spatibase-Signature","in":"header","required":true,"description":"t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">, keyed with the project's signing secret (the whole whsec_… text). To verify, compute it over the body exactly as received (before parsing JSON), compare in constant time, and refuse a t older than a few minutes so a captured message can't be replayed. Rotating the secret signs new messages with the new one at once","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"Spatibase-Event","in":"header","required":true,"description":"The event, as in the body's type","schema":{"type":"string","enum":["entries.unpublished"]}},{"name":"Spatibase-Delivery","in":"header","required":true,"description":"The delivery id, as in the body's id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEntriesUnpublished"}}}},"responses":{"2XX":{"description":"Received. Answer within 5 seconds. Anything else (3xx too: redirects aren't followed) is a failure: events are tried up to 3 times (after 1 s and 10 s, same id), the test ping once"}}}},"entries.deleted":{"post":{"operationId":"webhookEntriesDeleted","summary":"Entries were deleted (moved to the trash): live ones at a project publish, others at once","description":"Sent to the project's webhook address (API → Webhooks in the app; one per project). Body: ids only, read the content from the Delivery API. Headers also include Content-Type: application/json and User-Agent: Spatibase-Webhooks/1.","tags":["Webhooks"],"security":[{}],"parameters":[{"name":"Spatibase-Signature","in":"header","required":true,"description":"t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">, keyed with the project's signing secret (the whole whsec_… text). To verify, compute it over the body exactly as received (before parsing JSON), compare in constant time, and refuse a t older than a few minutes so a captured message can't be replayed. Rotating the secret signs new messages with the new one at once","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"Spatibase-Event","in":"header","required":true,"description":"The event, as in the body's type","schema":{"type":"string","enum":["entries.deleted"]}},{"name":"Spatibase-Delivery","in":"header","required":true,"description":"The delivery id, as in the body's id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEntriesDeleted"}}}},"responses":{"2XX":{"description":"Received. Answer within 5 seconds. Anything else (3xx too: redirects aren't followed) is a failure: events are tried up to 3 times (after 1 s and 10 s, same id), the test ping once"}}}},"groups.changed":{"post":{"operationId":"webhookGroupsChanged","summary":"A publish, or going back to a version, changed the live groups (created, renamed, deleted, members, order)","description":"Sent to the project's webhook address (API → Webhooks in the app; one per project). Body: ids only, read the content from the Delivery API. Headers also include Content-Type: application/json and User-Agent: Spatibase-Webhooks/1.","tags":["Webhooks"],"security":[{}],"parameters":[{"name":"Spatibase-Signature","in":"header","required":true,"description":"t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">, keyed with the project's signing secret (the whole whsec_… text). To verify, compute it over the body exactly as received (before parsing JSON), compare in constant time, and refuse a t older than a few minutes so a captured message can't be replayed. Rotating the secret signs new messages with the new one at once","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"Spatibase-Event","in":"header","required":true,"description":"The event, as in the body's type","schema":{"type":"string","enum":["groups.changed"]}},{"name":"Spatibase-Delivery","in":"header","required":true,"description":"The delivery id, as in the body's id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookGroupsChanged"}}}},"responses":{"2XX":{"description":"Received. Answer within 5 seconds. Anything else (3xx too: redirects aren't followed) is a failure: events are tried up to 3 times (after 1 s and 10 s, same id), the test ping once"}}}},"ping":{"post":{"operationId":"webhookPing","summary":"Send test message in the app (or POST …/webhook/test)","description":"Sent to the project's webhook address (API → Webhooks in the app; one per project). Body: ids only, read the content from the Delivery API. Headers also include Content-Type: application/json and User-Agent: Spatibase-Webhooks/1.","tags":["Webhooks"],"security":[{}],"parameters":[{"name":"Spatibase-Signature","in":"header","required":true,"description":"t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">, keyed with the project's signing secret (the whole whsec_… text). To verify, compute it over the body exactly as received (before parsing JSON), compare in constant time, and refuse a t older than a few minutes so a captured message can't be replayed. Rotating the secret signs new messages with the new one at once","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"Spatibase-Event","in":"header","required":true,"description":"The event, as in the body's type","schema":{"type":"string","enum":["ping"]}},{"name":"Spatibase-Delivery","in":"header","required":true,"description":"The delivery id, as in the body's id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPing"}}}},"responses":{"2XX":{"description":"Received. Answer within 5 seconds. Anything else (3xx too: redirects aren't followed) is a failure: events are tried up to 3 times (after 1 s and 10 s, same id), the test ping once"}}}}},"components":{"schemas":{"Geometry":{"description":"GeoJSON geometry (RFC 7946), coordinates rounded to 7 decimals","anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"Point"},"coordinates":{"description":"[longitude, latitude] in WGS84 degrees","minItems":2,"type":"array","items":{"type":"number"}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"description":"[longitude, latitude] in WGS84 degrees","minItems":2,"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"Polygon"},"coordinates":{"type":"array","items":{"type":"array","items":{"description":"[longitude, latitude] in WGS84 degrees","minItems":2,"type":"array","items":{"type":"number"}}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"MultiPoint"},"coordinates":{"type":"array","items":{"description":"[longitude, latitude] in WGS84 degrees","minItems":2,"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"MultiLineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"description":"[longitude, latitude] in WGS84 degrees","minItems":2,"type":"array","items":{"type":"number"}}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"MultiPolygon"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"description":"[longitude, latitude] in WGS84 degrees","minItems":2,"type":"array","items":{"type":"number"}}}}}},"required":["type","coordinates"],"additionalProperties":false}]},"Asset":{"description":"A file of an images or files element","examples":[{"id":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","url":"https://api.spatibase.com/assets/7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d/redwoods.jpg","filename":"redwoods.jpg","mimeType":"image/jpeg","size":793600,"width":1920,"height":1080,"alt":"Redwood trees"}],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"description":"Absolute URL of the original file. Images accept w, h, fit, fmt and q (SPEC §5 Assets)","type":"string","format":"uri"},"filename":{"type":"string"},"mimeType":{"type":"string"},"size":{"description":"Bytes","type":"integer"},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"alt":{"type":"string"}},"required":["id","url","filename","mimeType","size","width","height","alt"],"additionalProperties":false},"ReferenceStub":{"description":"A published entry a reference element points to (without include=references)","examples":[{"id":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","type":"region","title":"Northern California"}],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"title":{"type":"string"}},"required":["id","type","title"],"additionalProperties":false},"Link":{"examples":[{"url":"https://www.nps.gov/redw","label":"Park website"}],"type":"object","properties":{"url":{"type":"string","format":"uri"},"label":{"type":"string"}},"required":["url"],"additionalProperties":false},"Contact":{"examples":[{"name":"Ranger desk","email":"redw@example.org","address":{"city":"Crescent City","country":"US"}}],"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"website":{"type":"string"},"address":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"},"region":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":"string"}},"additionalProperties":false}},"additionalProperties":false},"Location":{"description":"A point value of a location element","examples":[{"lng":-124.02,"lat":41.3,"address":"Kuchel Visitor Center"}],"type":"object","properties":{"lng":{"type":"number","minimum":-180,"maximum":180},"lat":{"type":"number","minimum":-90,"maximum":90},"address":{"type":"string"}},"required":["lng","lat"],"additionalProperties":false},"Embed":{"examples":[{"url":"https://youtu.be/abc123","provider":"youtube","embedUrl":"https://www.youtube-nocookie.com/embed/abc123"}],"type":"object","properties":{"url":{"type":"string"},"provider":{"type":"string","enum":["youtube","vimeo","soundcloud","spotify","other"]},"embedUrl":{"description":"An iframe-ready URL for youtube, vimeo, spotify and soundcloud, else null","anyOf":[{"type":"string","format":"uri"},{"type":"null"}]}},"required":["url","provider","embedUrl"],"additionalProperties":false},"DateRange":{"description":"A date element with range: true, both ends in the element's mode","examples":[{"start":"2026-05-01","end":"2026-10-31"}],"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":false},"GeoFeatureCollection":{"description":"The value of a geo element","examples":[{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.01,41.21]},"properties":{"label":"Main lot"}}]}],"type":"object","properties":{"type":{"type":"string","const":"FeatureCollection"},"features":{"minItems":1,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","const":"Feature"},"geometry":{"$ref":"#/components/schemas/Geometry"},"properties":{"type":"object","properties":{"label":{"type":"string"},"featureType":{"description":"apiKey of the shape's Feature Type (see featureTypes)","type":"string"}},"additionalProperties":false}},"required":["type","geometry","properties"],"additionalProperties":false}}},"required":["type","features"],"additionalProperties":false},"FieldValue":{"description":"The value of one element, null when empty. Its shape depends on the element's kind and config (see the project's types): text, markdown, identifier, phone, color (#rrggbb) → string; text with editor \"json\" → the parsed JSON value; number → number; boolean → boolean; date → string (DateRange with range); choice → string (array of strings with multiple); link → Link; images, files → Asset[]; contact → Contact; reference → ReferenceStub (array with multiple; Entry with include=references); geo → GeoFeatureCollection; embed → Embed; location → Location.","anyOf":[{"$ref":"#/components/schemas/Asset"},{"$ref":"#/components/schemas/ReferenceStub"},{"$ref":"#/components/schemas/Link"},{"$ref":"#/components/schemas/Contact"},{"$ref":"#/components/schemas/Location"},{"$ref":"#/components/schemas/Embed"},{"$ref":"#/components/schemas/DateRange"},{"$ref":"#/components/schemas/GeoFeatureCollection"},{"description":"Any other JSON value: string, number, boolean, null, arrays (Asset[], ReferenceStub[], string[]), parsed JSON text, an Entry with include=references"}]},"Translations":{"description":"locale=*: the stored translations per language (title and text/markdown fields), only what exists; no fallback","examples":[{"de":{"title":"Redwood-Nationalpark","values":{"description":"Küstenwald mit riesigen Mammutbäumen"}}}],"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}}},"additionalProperties":false}},"Entry":{"description":"A published entry","examples":[{"id":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","type":"national-park","typePath":["park","national-park"],"title":"Redwood National Park","geometry":{"type":"Point","coordinates":[-124.0046,41.2132]},"fields":{"description":"The Redwood National and State Parks protect the tallest trees on Earth.","region":{"id":"a3e1f0c2-9d4b-4c7e-8f6a-1b2c3d4e5f60","type":"region","title":"Northern California"},"video":{"url":"https://youtu.be/abc123","provider":"youtube","embedUrl":"https://www.youtube-nocookie.com/embed/abc123"},"slug":"redwood-national-park","rating":4.9},"groups":["featured","coastal-road-trip"],"createdAt":"2026-03-02T09:15:00.000Z","updatedAt":"2026-04-11T16:40:12.000Z","publishedAt":"2026-04-11T16:40:12.000Z"}],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"description":"apiKey of the entry's Type","type":"string"},"typePath":{"description":"Type apiKeys from the root Type down to the entry's own","type":"array","items":{"type":"string"}},"title":{"type":"string"},"geometry":{"description":"The main location; null without one (always for Types with locationMode \"none\")","anyOf":[{"$ref":"#/components/schemas/Geometry"},{"type":"null"}]},"fields":{"description":"One property per effective element of the entry's Type (inherited first), keyed by element apiKey; empty values are null. Per-project shapes: GET …/types","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FieldValue"}},"groups":{"description":"apiKeys of the groups the entry is in","type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"description":"Equals publishedAt: draft saves never change Delivery output","type":"string","format":"date-time"},"publishedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"distance":{"description":"Meters from near or nearest (only with one of them)","type":"number"},"locale":{"description":"With locale=<code>: the language of this response (the default language's code when it is served)","type":"string"},"missingTranslations":{"description":"With locale=<code>: \"title\" and the apiKeys of the text fields that fall back to the default language (empty when complete, and for the default language)","type":"array","items":{"type":"string"}},"translations":{"description":"Only with locale=*","$ref":"#/components/schemas/Translations"}},"required":["id","type","typePath","title","geometry","fields","groups","createdAt","updatedAt","publishedAt"],"additionalProperties":false},"PageMeta":{"examples":[{"page":1,"limit":50,"total":120,"totalPages":3}],"type":"object","properties":{"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"totalPages":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["page","limit","total","totalPages"],"additionalProperties":false},"PageLinks":{"description":"Absolute URLs of this request with another page (the query is kept, an api_key too)","type":"object","properties":{"self":{"format":"uri","type":"string","pattern":"^[A-Za-z0-9\\-._~:/?#@!$&'()*+,;=%]+$"},"next":{"anyOf":[{"format":"uri","type":"string","pattern":"^[A-Za-z0-9\\-._~:/?#@!$&'()*+,;=%]+$"},{"type":"null"}]},"prev":{"anyOf":[{"format":"uri","type":"string","pattern":"^[A-Za-z0-9\\-._~:/?#@!$&'()*+,;=%]+$"},{"type":"null"}]}},"required":["self","next","prev"],"additionalProperties":false},"EntryList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Entry"}},"meta":{"$ref":"#/components/schemas/PageMeta"},"links":{"$ref":"#/components/schemas/PageLinks"}},"required":["data","meta","links"],"additionalProperties":false},"EntryResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Entry"}},"required":["data"],"additionalProperties":false},"EntryIndexItem":{"description":"One published entry in the compact index","type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"description":"apiKey of the entry's Type","type":"string"},"title":{"type":"string"},"bbox":{"description":"The main location's bounding box [minLng, minLat, maxLng, maxLat] (6 decimals; a point has min = max); null without one","anyOf":[{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]},{"type":"null"}]},"distance":{"description":"Meters from near or nearest (only with one of them)","type":"number"},"translations":{"description":"Only with locale=*: the title per language (only those that exist)","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"title":{"type":"string"}},"required":["title"],"additionalProperties":false}}},"required":["id","type","title","bbox"],"additionalProperties":false},"EntryIndex":{"description":"A compact list of published entries (id, type, title, bbox), for lists, search boxes and \"zoom to\" links","examples":[{"data":[{"id":"0b6f3c1e-5d2a-4c1b-9f3e-2a7d8c9e4f10","type":"national-park","title":"Redwood National Park","bbox":[-124.0046,41.2132,-124.0046,41.2132]}],"meta":{"page":1,"limit":1000,"total":1,"totalPages":1},"links":{"self":"https://api.spatibase.com/delivery/v1/projects/7d1c2a9e-4b3f-4e8a-9c2d-1f0e3b5a6c7d/entries.index?sort=title","next":null,"prev":null}}],"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EntryIndexItem"}},"meta":{"$ref":"#/components/schemas/PageMeta"},"links":{"$ref":"#/components/schemas/PageLinks"}},"required":["data","meta","links"],"additionalProperties":false},"EntryFeatureCollection":{"description":"Published entries with a main location as GeoJSON; meta and links are foreign members (RFC 7946 §6.1)","type":"object","properties":{"type":{"type":"string","const":"FeatureCollection"},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","const":"Feature"},"id":{"type":"string","format":"uuid"},"geometry":{"$ref":"#/components/schemas/Geometry"},"properties":{"description":"id, type and title (with languages: locale and missingTranslations, or translations with locale=*), then the entry's fields (as in Entry.fields)","type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"title":{"type":"string"},"locale":{"description":"With locale=<code>: the language of this response (the default language's code when it is served)","type":"string"},"missingTranslations":{"description":"With locale=<code>: \"title\" and the apiKeys of the text fields that fall back to the default language (empty when complete, and for the default language)","type":"array","items":{"type":"string"}},"translations":{"$ref":"#/components/schemas/Translations"}},"required":["id","type","title"],"additionalProperties":{"$ref":"#/components/schemas/FieldValue"}}},"required":["type","id","geometry","properties"],"additionalProperties":false}},"meta":{"$ref":"#/components/schemas/PageMeta"},"links":{"$ref":"#/components/schemas/PageLinks"}},"required":["type","features","meta","links"],"additionalProperties":false},"TypeElement":{"type":"object","properties":{"apiKey":{"type":"string"},"label":{"type":"string"},"kind":{"type":"string","enum":["text","markdown","number","boolean","date","choice","link","images","files","contact","color","reference","geo","identifier","embed","location","phone"]},"required":{"type":"boolean"},"helpText":{"type":"string"},"config":{"description":"Kind-specific settings (SPEC §3). Reference targets (targetTypes) and geo feature types (featureTypes) are listed by apiKey","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"inheritedFrom":{"description":"apiKey of the ancestor Type it is inherited from, null for own elements","anyOf":[{"type":"string"},{"type":"null"}]}},"required":["apiKey","label","kind","required","helpText","config","inheritedFrom"],"additionalProperties":false},"Type":{"description":"An enabled Type (tree order)","type":"object","properties":{"apiKey":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"parent":{"anyOf":[{"type":"string"},{"type":"null"}]},"path":{"type":"array","items":{"type":"string"}},"children":{"type":"array","items":{"type":"string"}},"locationMode":{"type":"string","enum":["required","optional","none"]},"geometryKinds":{"type":"array","items":{"type":"string","enum":["Point","LineString","Polygon"]}},"color":{"type":"string"},"entryCount":{"description":"Published entries of this Type itself; entries of its subtypes count for their own Type","type":"integer"},"elements":{"description":"Effective elements: inherited first, then own","type":"array","items":{"$ref":"#/components/schemas/TypeElement"}}},"required":["apiKey","name","description","parent","path","children","locationMode","geometryKinds","color","entryCount","elements"],"additionalProperties":false},"FeatureType":{"description":"A named, styled kind of shape in geo fields","type":"object","properties":{"apiKey":{"type":"string"},"name":{"type":"string"},"geometryKind":{"type":"string","enum":["Point","LineString","Polygon"]},"icon":{"anyOf":[{"type":"string","enum":["pin","flag","tree","mountain","tent","water","building","route","star","parking","info"]},{"type":"null"}]},"color":{"type":"string"}},"required":["apiKey","name","geometryKind","icon","color"],"additionalProperties":false},"Group":{"type":"object","properties":{"apiKey":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"entryCount":{"description":"Published entries of enabled Types in the group","type":"integer"}},"required":["apiKey","name","description","entryCount"],"additionalProperties":false},"GroupWithEntries":{"type":"object","properties":{"data":{"type":"object","properties":{"apiKey":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}},"required":["apiKey","name","description"],"additionalProperties":false},"entries":{"description":"In the group's manual order","type":"array","items":{"$ref":"#/components/schemas/Entry"}},"meta":{"$ref":"#/components/schemas/PageMeta"},"links":{"$ref":"#/components/schemas/PageLinks"}},"required":["data","entries","meta","links"],"additionalProperties":false},"Project":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"contentVersion":{"description":"Goes up with every change to Delivery output","type":"integer"},"locales":{"description":"Only for projects with languages: their codes, the first is the default","type":"array","items":{"type":"string"}},"defaultLocale":{"description":"Only for projects with languages: the default language's code","type":"string"},"types":{"type":"array","items":{"$ref":"#/components/schemas/Type"}},"groups":{"type":"array","items":{"$ref":"#/components/schemas/Group"}},"featureTypes":{"type":"array","items":{"$ref":"#/components/schemas/FeatureType"}}},"required":["id","name","description","contentVersion","types","groups","featureTypes"],"additionalProperties":false},"TileJson":{"description":"TileJSON 3.0.0 for the vector tiles","type":"object","properties":{"tilejson":{"type":"string","const":"3.0.0"},"name":{"type":"string"},"description":{"type":"string"},"version":{"description":"The project's contentVersion","type":"string"},"scheme":{"type":"string","const":"xyz"},"tiles":{"description":"Tile URL template; with ?api_key= in the request it carries the key too","type":"array","items":{"type":"string"}},"minzoom":{"type":"integer","minimum":0,"maximum":22},"maxzoom":{"type":"integer","minimum":0,"maximum":22},"bounds":{"description":"minLng, minLat, maxLng, maxLat of the published main locations","minItems":4,"maxItems":4,"type":"array","items":{"type":"number"}},"center":{"minItems":3,"maxItems":3,"type":"array","items":{"type":"number"}},"vector_layers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","const":"entries"},"description":{"type":"string"},"fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string","enum":["String","Number","Boolean"]}},"minzoom":{"type":"integer","minimum":0,"maximum":22},"maxzoom":{"type":"integer","minimum":0,"maximum":22}},"required":["id","description","fields","minzoom","maxzoom"],"additionalProperties":false}}},"required":["tilejson","name","version","scheme","tiles","minzoom","maxzoom","bounds","center","vector_layers"],"additionalProperties":false},"Error":{"examples":[{"error":{"code":"not_found","message":"Entry \"…\" not found (or not published) in this project"}}],"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["bad_request","not_found","method_not_allowed","internal_error","api_key_required","api_key_invalid","rate_limited","locale_unknown","project_expired"]},"message":{"description":"A plain-language explanation, safe to show developers","type":"string"},"locales":{"description":"With locale_unknown: the project's locales","type":"array","items":{"type":"string"}}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false},"OpenApiDocument":{"description":"This document","type":"object","properties":{"openapi":{"type":"string","const":"3.1.0"},"info":{"type":"object","properties":{"title":{"type":"string"},"version":{"type":"string"}},"required":["title","version"],"additionalProperties":{}},"paths":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["openapi","info","paths"],"additionalProperties":{}},"WebhookEntriesPublished":{"description":"Entries went live or changed: a project publish, or going back to a version","examples":[{"id":"1042","type":"entries.published","projectId":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","createdAt":"2026-10-15T09:30:00.000Z","contentVersion":57,"entryIds":["a3e1f0c2-9d4b-4c7e-8f6a-1b2c3d4e5f60"]}],"type":"object","properties":{"id":{"description":"The delivery id (also in Spatibase-Delivery). Retries keep it: use it to ignore duplicates","type":"string","pattern":"^\\d+$"},"type":{"type":"string","const":"entries.published"},"projectId":{"type":"string","format":"uuid"},"createdAt":{"description":"When the event was created","type":"string","format":"date-time"},"contentVersion":{"description":"The project's contentVersion when the message was sent: compare it with the last one you saw","type":"integer"},"version":{"description":"The number of the project version that caused this (publish or going back)","type":"integer"},"entryIds":{"description":"Ids of the entries (at most 200: a longer list is cut, re-read what you need)","minItems":1,"maxItems":200,"type":"array","items":{"type":"string","format":"uuid"}}},"required":["id","type","projectId","createdAt","contentVersion","entryIds"],"additionalProperties":false},"WebhookEntriesUnpublished":{"description":"Entries came off the live API: a project publish, or going back to a version","examples":[{"id":"1042","type":"entries.unpublished","projectId":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","createdAt":"2026-10-15T09:30:00.000Z","contentVersion":57,"entryIds":["a3e1f0c2-9d4b-4c7e-8f6a-1b2c3d4e5f60"]}],"type":"object","properties":{"id":{"description":"The delivery id (also in Spatibase-Delivery). Retries keep it: use it to ignore duplicates","type":"string","pattern":"^\\d+$"},"type":{"type":"string","const":"entries.unpublished"},"projectId":{"type":"string","format":"uuid"},"createdAt":{"description":"When the event was created","type":"string","format":"date-time"},"contentVersion":{"description":"The project's contentVersion when the message was sent: compare it with the last one you saw","type":"integer"},"version":{"description":"The number of the project version that caused this (publish or going back)","type":"integer"},"entryIds":{"description":"Ids of the entries (at most 200: a longer list is cut, re-read what you need)","minItems":1,"maxItems":200,"type":"array","items":{"type":"string","format":"uuid"}}},"required":["id","type","projectId","createdAt","contentVersion","entryIds"],"additionalProperties":false},"WebhookEntriesDeleted":{"description":"Entries were deleted (moved to the trash): live ones at a project publish, others at once","examples":[{"id":"1042","type":"entries.deleted","projectId":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","createdAt":"2026-10-15T09:30:00.000Z","contentVersion":57,"entryIds":["a3e1f0c2-9d4b-4c7e-8f6a-1b2c3d4e5f60"]}],"type":"object","properties":{"id":{"description":"The delivery id (also in Spatibase-Delivery). Retries keep it: use it to ignore duplicates","type":"string","pattern":"^\\d+$"},"type":{"type":"string","const":"entries.deleted"},"projectId":{"type":"string","format":"uuid"},"createdAt":{"description":"When the event was created","type":"string","format":"date-time"},"contentVersion":{"description":"The project's contentVersion when the message was sent: compare it with the last one you saw","type":"integer"},"version":{"description":"The number of the project version that caused this (publish or going back)","type":"integer"},"entryIds":{"description":"Ids of the entries (at most 200: a longer list is cut, re-read what you need)","minItems":1,"maxItems":200,"type":"array","items":{"type":"string","format":"uuid"}}},"required":["id","type","projectId","createdAt","contentVersion","entryIds"],"additionalProperties":false},"WebhookGroupsChanged":{"description":"A publish, or going back to a version, changed the live groups (created, renamed, deleted, members, order)","examples":[{"id":"1042","type":"groups.changed","projectId":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","createdAt":"2026-10-15T09:30:00.000Z","contentVersion":57,"groupIds":["a3e1f0c2-9d4b-4c7e-8f6a-1b2c3d4e5f60"],"entryIds":["a3e1f0c2-9d4b-4c7e-8f6a-1b2c3d4e5f60"]}],"type":"object","properties":{"id":{"description":"The delivery id (also in Spatibase-Delivery). Retries keep it: use it to ignore duplicates","type":"string","pattern":"^\\d+$"},"type":{"type":"string","const":"groups.changed"},"projectId":{"type":"string","format":"uuid"},"createdAt":{"description":"When the event was created","type":"string","format":"date-time"},"contentVersion":{"description":"The project's contentVersion when the message was sent: compare it with the last one you saw","type":"integer"},"version":{"description":"The number of the project version that caused this (publish or going back)","type":"integer"},"groupIds":{"description":"Ids of the groups (at most 200: a longer list is cut, re-read what you need)","minItems":1,"maxItems":200,"type":"array","items":{"type":"string","format":"uuid"}},"entryIds":{"description":"Ids of the entries whose membership changed (at most 200: a longer list is cut, re-read what you need)","minItems":1,"maxItems":200,"type":"array","items":{"type":"string","format":"uuid"}}},"required":["id","type","projectId","createdAt","contentVersion","groupIds"],"additionalProperties":false},"WebhookPing":{"description":"Send test message in the app (or POST …/webhook/test)","examples":[{"id":"1042","type":"ping","projectId":"7f1c2a9e-4b3d-4e8a-9c21-5d6f8a0b1c2d","createdAt":"2026-10-15T09:30:00.000Z","contentVersion":57}],"type":"object","properties":{"id":{"description":"The delivery id (also in Spatibase-Delivery). Retries keep it: use it to ignore duplicates","type":"string","pattern":"^\\d+$"},"type":{"type":"string","const":"ping"},"projectId":{"type":"string","format":"uuid"},"createdAt":{"description":"When the event was created","type":"string","format":"date-time"},"contentVersion":{"description":"The project's contentVersion when the message was sent: compare it with the last one you saw","type":"integer"},"version":{"description":"The number of the project version that caused this (publish or going back)","type":"integer"}},"required":["id","type","projectId","createdAt","contentVersion"],"additionalProperties":false}},"parameters":{"projectId":{"name":"projectId","in":"path","required":true,"description":"The project's id (Settings → API in the app)","schema":{"type":"string","format":"uuid"}},"groupKey":{"name":"groupKey","in":"path","required":true,"description":"The group's apiKey","schema":{"type":"string"}},"entryId":{"name":"entryId","in":"path","required":true,"description":"The entry's id","schema":{"type":"string","format":"uuid"}},"z":{"name":"z","in":"path","required":true,"description":"Zoom level","schema":{"type":"integer","minimum":0,"maximum":22},"example":10},"x":{"name":"x","in":"path","required":true,"description":"Tile column, 0 … 2^z − 1","schema":{"type":"integer","minimum":0},"example":159},"y":{"name":"y","in":"path","required":true,"description":"Tile row, 0 … 2^z − 1","schema":{"type":"integer","minimum":0},"example":383},"ifNoneMatch":{"name":"If-None-Match","in":"header","description":"An ETag from an earlier response: 304 when nothing changed","schema":{"type":"string"}},"locale":{"name":"locale","in":"query","description":"Projects with languages: the language of the response, a code of the project's `locales` (e.g. de). Title and text fields (and choice option labels) fall back to the default language field by field, and the entry says so in `missingTranslations`. `*` returns the default language plus all stored translations in `translations`. Without it the best match of the `Accept-Language` header is served (quality values honoured), else the default language. An unknown code → 400 locale_unknown. The language is part of the ETag. Projects without languages: nothing changes","schema":{"type":"string"},"example":"de"},"page":{"name":"page","in":"query","description":"Page number","schema":{"type":"integer","minimum":1,"maximum":1000000,"default":1},"example":1},"limit":{"name":"limit","in":"query","description":"Entries per page (default 50, max 500; above the max → 400)","schema":{"type":"integer","minimum":1,"maximum":500,"default":50},"example":50},"geojsonLimit":{"name":"limit","in":"query","description":"Entries per page (default 1000, max 5000; above the max → 400)","schema":{"type":"integer","minimum":1,"maximum":5000,"default":1000},"example":1000},"include":{"name":"include","in":"query","description":"references: expand reference fields to full published entries (one level)","schema":{"type":"string","enum":["references"]}},"type":{"name":"type","in":"query","description":"Type apiKeys: entries of any of them. Includes subtypes unless subtypes=false. Unknown type → 400. Repeat the parameter for more values; a comma-separated list works too","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["park","region"]},"subtypes":{"name":"subtypes","in":"query","description":"false: with type, match only exactly those Types","schema":{"type":"boolean","default":true}},"group":{"name":"group","in":"query","description":"Group apiKeys: entries in any of them. Unknown group → 400. Repeat the parameter for more values; a comma-separated list works too","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["featured"]},"q":{"name":"q","in":"query","description":"Case-insensitive match on the title, in the default and the served language (max 500 characters)","schema":{"type":"string","maxLength":500}},"filter":{"name":"filter","in":"query","description":"Filter by element values, keyed by element apiKey: `filter[<apiKey>]=<value>` (equality) or `filter[<apiKey>][<op>]=<value>`. Operators: eq, ne, gt, gte, lt, lte (number and date), in (any of: a comma list or the parameter repeated, max 100 values), contains (case-insensitive), exists (true/false). Values max 500 characters (in: each value). For a choice with multiple, eq means \"contains this value\"; for a reference the value is the target entry id. Allowed operators per element kind:\n- text: eq, ne, in, contains, exists\n- markdown: eq, ne, in, contains, exists\n- color: eq, ne, in, exists\n- number: eq, ne, gt, gte, lt, lte, in, exists\n- date: eq, ne, gt, gte, lt, lte, in, exists\n- boolean: eq, ne, exists\n- choice: eq, ne, in, exists\n- link: eq, ne, in, contains, exists\n- contact: contains, exists\n- images: exists\n- files: exists\n- reference: eq, ne, in, exists\n- identifier: eq, ne, in, contains, exists\n- embed: exists\n- geo: exists\n- phone: eq, ne, in, contains, exists\n- location: exists\nText with editor \"json\" and date ranges: exists only. Multiple references: eq, in, exists. Unknown apiKeys, operators or values → 400 with the allowed ones. This generic spec can't list each project's filter keys; the project's typed spec (…/projects/{projectId}/openapi.json) has a named parameter per element and operator.","schema":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{"type":"string"}}]}},"style":"deepObject","explode":true,"examples":{"rating":{"value":{"rating":{"gte":"4"}}},"open":{"value":{"open":"true"}}}},"bbox":{"name":"bbox","in":"query","description":"minLng,minLat,maxLng,maxLat: entries whose main location intersects the box","schema":{"type":"string"},"example":"-125,40,-120,42"},"near":{"name":"near","in":"query","description":"lng,lat: entries within `radius` of the point; each entry gets `distance` (meters)","schema":{"type":"string"},"example":"-124.0,41.2"},"radius":{"name":"radius","in":"query","description":"Meters around near (needs near; default 10000, max 20037509)","schema":{"type":"number","exclusiveMinimum":0,"maximum":20037509,"default":10000},"example":5000},"within":{"name":"within","in":"query","description":"A GeoJSON Polygon or MultiPolygon (URL-encoded, max 8000 characters): entries whose main location intersects it. Not together with withinEntry","schema":{"type":"string","maxLength":8000}},"withinEntry":{"name":"withinEntry","in":"query","description":"Entries whose main location intersects the area of this published entry's main location (the entry itself is left out)","schema":{"type":"string","format":"uuid"}},"withinField":{"name":"withinField","in":"query","description":"With withinEntry: use the area drawn in this geo element of that entry instead","schema":{"type":"string"}},"nearest":{"name":"nearest","in":"query","description":"lng,lat: every entry with a main location, closest first, with no distance limit (page through them); each entry gets `distance` (meters). Not together with near or radius; sort may only be distance","schema":{"type":"string"},"example":"-124.0,41.2"},"contains":{"name":"contains","in":"query","description":"lng,lat: entries whose main location is an area (Polygon or MultiPolygon) covering the point, boundary included, e.g. \"which delivery zone is this address in?\". Points and lines never match","schema":{"type":"string"},"example":"-124.0,41.2"},"containsField":{"name":"containsField","in":"query","description":"With contains: match the areas drawn in this geo element instead of the main location (e.g. a store's delivery area). Not index-assisted: combine it with type or other filters on large projects","schema":{"type":"string"}},"sort":{"name":"sort","in":"query","description":"Sort keys in order: title, createdAt, updatedAt, publishedAt, distance (needs near or nearest), group (needs exactly one group: the manual group order). Prefix with - for descending. Default -publishedAt. Repeat the parameter for more values; a comma-separated list works too","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["-publishedAt","title"]}},"headers":{"ETag":{"description":"Weak ETag of this response; send it as If-None-Match","schema":{"type":"string"}},"Cache-Control":{"description":"See the caching notes in the description","schema":{"type":"string"}},"WWW-Authenticate":{"description":"Bearer realm=\"Spatibase Delivery API\" (with error=\"invalid_token\" for a bad key)","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait","schema":{"type":"integer"}}},"responses":{"NotModified":{"description":"Not modified: the If-None-Match ETag is still current (no body)","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}}},"BadRequest":{"description":"A query parameter is invalid (bad_request); the message says which and how to fix it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The project is private: no key (api_key_required) or a key that isn't valid for it (api_key_invalid)","headers":{"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such project, entry or group (not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Gone":{"description":"A temporary project that expired because nobody kept it (project_expired); answered for 30 days","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooManyRequests":{"description":"Too many failed key checks from this client (rate_limited)","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A read or edit key of the project: Authorization: Bearer sb_live_…"},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"A read or edit key of the project"},"apiKeyQuery":{"type":"apiKey","in":"query","name":"api_key","description":"A read key only (edit keys are refused in URLs). Ends up in logs: prefer a header"}}}}