{"openapi":"3.1.0","info":{"title":"DaycareMap BC API","version":"1","description":"The read API is free, needs no key and reads only. The admin routes under /admin/ need an admin session. Every read response carries `meta.attribution` and `meta.sources`."},"servers":[{"url":"https://admin.daycaremap.fyi/api/v1"}],"paths":{"/listings":{"get":{"summary":"Search and list listings","operationId":"getListings","parameters":[{"name":"q","in":"query","required":false,"description":"Search text, with at least one letter or digit. Matches name, categories, attributes, description and place. Typos fall back to close matches.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"country","in":"query","required":false,"description":"ISO 3166-1 alpha-2 country code, any case. A country the directory does not cover, as listed in /places, gives 400.","schema":{"type":"string","pattern":"^[A-Za-z]{2}$"}},{"name":"region","in":"query","required":false,"description":"Region slug, from /places.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"}},{"name":"city","in":"query","required":false,"description":"City slug, from /places.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"}},{"name":"category","in":"query","required":false,"description":"Category slug, from /categories. A parent slug also matches its children.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"}},{"name":"sort","in":"query","required":false,"description":"`relevance` needs `q` and is the default with it. `name` is the default without `q`. `recent` is newest first.","schema":{"type":"string","enum":["relevance","name","recent"]}},{"name":"limit","in":"query","required":false,"description":"Items per page.","schema":{"default":20,"type":"integer","minimum":1,"maximum":50}},{"name":"cursor","in":"query","required":false,"description":"`page.nextCursor` from the previous response. Opaque. Send the same filters and `limit`.","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","page","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ListingItem"}},"page":{"$ref":"#/components/schemas/Page"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/listings/{slug}":{"get":{"summary":"Read one listing","operationId":"getListingsBySlug","parameters":[{"name":"slug","in":"path","required":true,"description":"The listing slug.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Listing"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record. `code` is `not_found`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/places":{"get":{"summary":"List countries, regions and cities with counts","operationId":"getPlaces","parameters":[],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlaceNode"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/categories":{"get":{"summary":"List the category tree with counts","operationId":"getCategories","parameters":[],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CategoryNode"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sources":{"get":{"summary":"List the data sources with license and refresh","operationId":"getSources","parameters":[],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Source"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/stats":{"get":{"summary":"Counts and the last update","operationId":"getStats","parameters":[],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Stats"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=60, s-maxage=300, stale-while-revalidate=600`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/products":{"get":{"summary":"List the live affiliate products, all or one category's","operationId":"getProducts","parameters":[{"name":"category","in":"query","required":false,"description":"A product category slug.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/products/{slug}":{"get":{"summary":"Read one affiliate product","operationId":"getProductsBySlug","parameters":[{"name":"slug","in":"path","required":true,"description":"The product slug.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Product"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record. `code` is `not_found`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/product-categories":{"get":{"summary":"List the product categories with their product counts","operationId":"getProductCategories","parameters":[],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProductCategory"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/academy/courses":{"get":{"summary":"List the published academy courses","operationId":"getAcademyCourses","parameters":[{"name":"level","in":"query","required":false,"description":"Only courses of this level.","schema":{"type":"string","enum":["foundations","practitioner","specialist"]}},{"name":"limit","in":"query","required":false,"description":"Most courses to return.","schema":{"default":50,"type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","page","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Course"}},"page":{"$ref":"#/components/schemas/Page"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/academy/courses/{slug}":{"get":{"summary":"Read one published course with its objectives, lessons and FAQ. No exam questions or answers","operationId":"getAcademyCoursesBySlug","parameters":[{"name":"slug","in":"path","required":true,"description":"The course slug.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/CourseDetail"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=30`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record. `code` is `not_found`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/certificates/{code}":{"get":{"summary":"Verify one certificate of completion by its code","operationId":"getCertificatesByCode","parameters":[{"name":"code","in":"path","required":true,"description":"A certificate code. Dashes, spaces and case do not matter.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=60`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Certificate"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"The `If-None-Match` validator still matches. No body.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"ETag":{"description":"A weak validator. Send it back as `If-None-Match`.","schema":{"type":"string"}},"Cache-Control":{"description":"`public, max-age=0, s-maxage=60`.","schema":{"type":"string"}}}},"400":{"description":"A parameter is unknown, repeated, empty or invalid. `code` is `invalid_parameter` or `invalid_cursor`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record. `code` is `not_found`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the limit of 300 requests per 60 seconds. `code` is `rate_limited`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 60.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error on the server. `code` is `internal_error`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is briefly overloaded. `code` is `unavailable`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per window. 300.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The policy as `limit;w=seconds`. `300;w=60`.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"`*`. Any origin may read.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait. 5.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/backlinks/export":{"get":{"summary":"Export directory submissions as CSV. Session admin only. An unknown status filter is ignored.","operationId":"getAdminBacklinksExport","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/test-sends":{"post":{"summary":"Send one confirmed test call, text or postcard. Needs the leads:write scope or an admin session.","operationId":"postAdminTestSends","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"channel":{"type":"string","const":"call"},"to":{"type":"string","minLength":7,"maxLength":60}},"required":["channel","to"]},{"type":"object","properties":{"channel":{"type":"string","const":"text"},"to":{"type":"string","minLength":7,"maxLength":60},"body":{"type":"string","minLength":1,"maxLength":600}},"required":["channel","to","body"]},{"type":"object","properties":{"channel":{"type":"string","const":"postcard"},"to":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"line1":{"type":"string","minLength":1,"maxLength":120},"line2":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"city":{"type":"string","minLength":1,"maxLength":80},"state":{"type":"string","minLength":2,"maxLength":2},"zip":{"type":"string","pattern":"^\\d{5}(-\\d{4})?$"}},"required":["name","line1","city","state","zip"]},"message":{"type":"string","minLength":1,"maxLength":500}},"required":["channel","to","message"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"provider":{"type":"string"},"to":{"type":"string"},"status":{"type":"string"},"providerId":{"type":"string"},"costCents":{"type":"number"},"currency":{"type":"string"},"expectedDelivery":{"type":["string","null"]}},"required":["ok","provider","to","status"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/voice/preview":{"post":{"summary":"Preview a fixed spoken line with the selected voice. Session admin only. Success returns raw audio bytes.","operationId":"postAdminVoicePreview","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","enum":["telnyx","xai","elevenlabs"]},"voiceId":{"type":"string","minLength":1,"maxLength":160},"personaName":{"type":"string","maxLength":80},"language":{"type":"string","maxLength":32},"speed":{"type":"number","minimum":0.25,"maximum":2}},"required":["provider","voiceId"]}}}},"responses":{"200":{"description":"Success.","content":{"audio/*":{"schema":{"description":"Raw audio bytes."}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/agent-runs":{"get":{"summary":"The agent runs, newest first, filtered by status and agent. Pass the cursor of the page before to read on. Needs the agent:run scope or an admin session.","operationId":"getAdminAgentRuns","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["running","succeeded","failed","cancelled"]}},{"name":"agent","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(run_[a-z0-9_]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"agent":{"type":"string"},"command":{"type":"string"},"input":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"status":{"type":"string","enum":["running","succeeded","failed","cancelled"]},"outputUrl":{"type":["string","null"]},"error":{"type":["string","null"]},"retryable":{"type":"boolean"},"startedAt":{"type":"string"},"finishedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","agent","command","input","status","outputUrl","error","retryable","startedAt","finishedAt","createdAt"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Record one agent run: agent, command, input, status, output link, error and retry flag. A run that is not running has finished now. Needs the agent:run scope or an admin session.","operationId":"postAdminAgentRuns","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent":{"type":"string","minLength":1,"maxLength":80},"command":{"type":"string","minLength":1,"maxLength":500},"input":{"default":{},"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"status":{"default":"running","type":"string","enum":["running","succeeded","failed","cancelled"]},"outputUrl":{"type":"string","maxLength":2000,"format":"uri"},"error":{"type":"string","minLength":1,"maxLength":2000},"retryable":{"default":false,"type":"boolean"},"startedAt":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["agent","command"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok","id"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/agent-runs/{id}":{"patch":{"summary":"Finish a running agent run with its outcome: succeeded, failed or cancelled, with the output link, the error and the retry flag. A run that has finished is a 409. Needs the agent:run scope or an admin session.","operationId":"patchAdminAgentRunsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["succeeded","failed","cancelled"]},"outputUrl":{"type":"string","maxLength":2000,"format":"uri"},"error":{"type":"string","minLength":1,"maxLength":2000},"retryable":{"default":false,"type":"boolean"}},"required":["status"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"status":{"type":"string","enum":["running","succeeded","failed","cancelled"]},"finishedAt":{"type":"string"}},"required":["ok","id","status","finishedAt"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/apps/{slug}":{"put":{"summary":"Create or replace one app. Needs the apps:write scope or an admin session.","operationId":"putAdminAppsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"website":{"type":"string","maxLength":2000,"format":"uri"},"tagline":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"description":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}]},"iconUrl":{"anyOf":[{"type":"string","maxLength":2000,"format":"uri"},{"type":"null"}]},"pricingModel":{"default":"free","type":"string","enum":["free","freemium","paid","subscription"]},"priceAmount":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"priceCurrency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}]},"priceNote":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"categorySlug":{"anyOf":[{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},{"type":"null"}]},"platforms":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":40}},"featured":{"default":false,"type":"boolean"},"featuredRank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"draft","type":"string","enum":["draft","published","archived"]},"sourceUrl":{"anyOf":[{"type":"string","maxLength":2000,"format":"uri"},{"type":"null"}]}},"required":["name","website"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete one app. Needs the apps:write scope or an admin session.","operationId":"deleteAdminAppsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/audit-log":{"get":{"summary":"The audit log, newest first.","operationId":"getAdminAuditLog","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/backup/export":{"post":{"summary":"Snapshot the site database into private storage and record the run. Takes no request body.","operationId":"postAdminBackupExport","parameters":[],"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"run":{}},"required":["run"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/backup/status":{"get":{"summary":"The backup ledger and the last export.","operationId":"getAdminBackupStatus","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{}},"required":["status"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/categories/{slug}":{"put":{"summary":"Create or replace one category. Needs the categories:write scope or an admin session.","operationId":"putAdminCategoriesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"names":{"type":"object","propertyNames":{"type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]+)*$"},"additionalProperties":{"type":"string","minLength":1,"maxLength":120}},"description":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"parentSlug":{"anyOf":[{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},{"type":"null"}]},"sortOrder":{"default":0,"type":"integer","minimum":0,"maximum":9999}},"required":["name"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete one category that has no children and no listings. Needs the categories:write scope or an admin session.","operationId":"deleteAdminCategoriesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/billing/subscriptions":{"get":{"summary":"The subscriptions of this site, newest first, with the failed-payment state: when the failure began and when the grace period ends. Reads the local table. Needs the billing:read scope or an admin session.","operationId":"getAdminBillingSubscriptions","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","trialing","past_due","unpaid","canceled","incomplete","incomplete_expired","paused"]}},{"name":"dunning","in":"query","required":false,"schema":{"type":"string","enum":["grace","grace_ended"]}},{"name":"listing","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(subn_[A-Za-z0-9_]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/billing/subscriptions/{id}":{"patch":{"summary":"Cancel one subscription of this site at Stripe, now or at the end of the paid period, then sync it. Needs the billing:write scope or an admin session.","operationId":"patchAdminBillingSubscriptionsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"atPeriodEnd":{"type":"boolean"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"status":{"type":"string"},"cancelAtPeriodEnd":{"type":"boolean"},"tier":{"type":"string"}},"required":["ok","id","status","cancelAtPeriodEnd","tier"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/billing/invoices":{"get":{"summary":"The Stripe invoices of one subscription of this site, newest first. Needs the billing:read scope or an admin session.","operationId":"getAdminBillingInvoices","parameters":[{"name":"subscription","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^in_[A-Za-z0-9_]{1,80}$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptionId":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"currency":{"type":"string"},"totalCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"amountPaidCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string"}},"required":["id","status","currency","totalCents","amountPaidCents","createdAt"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["subscriptionId","items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/billing/invoices/{id}/refund":{"post":{"summary":"Refund the payment of one subscription invoice of this site, in part or in full. A full refund revokes the tier and cancels the subscription. Needs the billing:write scope or an admin session.","operationId":"postAdminBillingInvoicesByIdRefund","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"integer","minimum":1,"maximum":100000000},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"invoiceId":{"type":"string"},"chargeId":{"type":"string"},"refundId":{"type":"string"},"amount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"currency":{"type":"string"},"fullyRefunded":{"type":"boolean"},"outcome":{"type":"string"}},"required":["ok","invoiceId","chargeId","refundId","amount","currency","fullyRefunded","outcome"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/billing/retry":{"post":{"summary":"Retry the failed renewal of a past_due subscription: pay its newest open invoice now. A declined card answers 200 with paid false. Needs the billing:write scope or an admin session.","operationId":"postAdminBillingRetry","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subscriptionId":{"type":"string","minLength":1,"maxLength":64},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["subscriptionId","reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"subscriptionId":{"type":"string"},"invoiceId":{"type":"string"},"paid":{"type":"boolean"},"status":{"type":"string"},"tier":{"type":"string"},"error":{"type":"string"}},"required":["ok","subscriptionId","invoiceId","paid","status","tier"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/claims/{id}":{"post":{"summary":"Approve or reject a manual claim.","operationId":"postAdminClaimsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"approve"},"currentOwnerId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"note":{"type":"string","maxLength":500}},"required":["action","currentOwnerId"]},{"type":"object","properties":{"action":{"type":"string","const":"reject"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","reason"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"listingId":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Preview deletion by default. Apply with dryRun false and confirm equal to the claim id. A verified claim that sets the listing's current owner answers 409: revoke the owner first. Needs the submissions:review scope or an admin session.","operationId":"deleteAdminClaimsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"default":true,"type":"boolean"},"confirm":{"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"listingId":{"type":"string"},"dryRun":{"type":"boolean"},"applied":{"type":"boolean"},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"listingId":{"type":"string"},"status":{"type":"string","enum":["pending","verified","rejected","expired"]},"action":{"type":"string","const":"deleted"}},"required":["id","listingId","status","action"],"additionalProperties":false}}},"required":["ok","listingId","dryRun","applied","rows"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{id}/owner":{"post":{"summary":"Assign the owner of a listing by admin fiat: a verified manual claim, no email flow. An active paid subscription blocks it (409). Needs the submissions:review scope or an admin session.","operationId":"postAdminListingsByIdOwner","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","minLength":1,"maxLength":64},"email":{"type":"string","maxLength":320,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"note":{"type":"string","maxLength":500}},"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"listingId":{"type":"string"},"claimId":{"type":"string"}},"required":["ok","listingId"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Revoke the owner of a listing: its verified claim expires and claimed_by clears. 409 when the listing has no owner. Needs the submissions:review scope or an admin session.","operationId":"deleteAdminListingsByIdOwner","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"listingId":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/claims":{"get":{"summary":"Claims, newest first. Filter by listing (id or slug) and status. Needs the submissions:review scope or an admin session.","operationId":"getAdminClaims","parameters":[{"name":"listing","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","verified","rejected","expired"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(clm_[A-Za-z0-9_]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/guides":{"get":{"summary":"List every guide of this site, drafts included. Needs the content:read scope or an admin session.","operationId":"getAdminGuides","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create or replace one guide from a Markdown file. Needs the content:write scope or an admin session.","operationId":"postAdminGuides","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"content":{"type":"string","minLength":1,"maxLength":150000},"published":{"type":"boolean"},"gated":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":0,"maximum":10000}},"required":["slug","content"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/guides/{slug}":{"get":{"summary":"Read one guide. Needs the content:read scope or an admin session.","operationId":"getAdminGuidesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Replace one guide from a Markdown file. Needs the content:write scope or an admin session.","operationId":"putAdminGuidesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":150000},"published":{"type":"boolean"},"gated":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":0,"maximum":10000}},"required":["content"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Publish or unpublish one guide. Needs the content:write scope or an admin session.","operationId":"patchAdminGuidesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"published":{"type":"boolean"}},"required":["published"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete one guide. Needs the content:write scope or an admin session.","operationId":"deleteAdminGuidesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/crm/export":{"get":{"summary":"The filtered contacts as CSV.","operationId":"getAdminCrmExport","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"string"}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/crm/{id}":{"patch":{"summary":"Edit a contact's fields.","operationId":"patchAdminCrmById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":60},"company":{"type":"string","maxLength":200},"status":{"type":"string","enum":["lead","active","customer","lost"]},"tags":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":40}}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Add a note to a contact.","operationId":"postAdminCrmById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":2000}},"required":["body"]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Remove a contact.","operationId":"deleteAdminCrmById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/edits/{id}":{"post":{"summary":"Accept, reject or spam a suggestion.","operationId":"postAdminEditsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"accept"},"note":{"type":"string","maxLength":500}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"reject"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","reason"]},{"type":"object","properties":{"action":{"type":"string","const":"spam"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","reason"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"listingId":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/enrichment/requests":{"get":{"summary":"The enrichment requests of the site, newest first, filtered by status. Pass the cursor of the page before to read on. Needs the content:read scope or an admin session.","operationId":"getAdminEnrichmentRequests","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","running","done","failed"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(enr_[a-z0-9_]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"site":{"type":"string"},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"status":{"type":"string","enum":["open","running","done","failed"]},"listingsDone":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"error":{"type":["string","null"]},"createdAt":{"type":"string"},"startedAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]}},"required":["id","site","limit","status","listingsDone","error","createdAt","startedAt","finishedAt"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/enrichment/requests/{id}":{"patch":{"summary":"Move one enrichment request on: open to running, then running to done (with listingsDone) or failed (with error). An open request may also fail. Any other step is a 409. Needs the content:write scope or an admin session.","operationId":"patchAdminEnrichmentRequestsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["running","done","failed"]},"listingsDone":{"type":"integer","minimum":0,"maximum":100000},"error":{"type":"string","minLength":1,"maxLength":2000}},"required":["status"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"status":{"type":"string","enum":["open","running","done","failed"]}},"required":["ok","id","status"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/ads/campaigns":{"post":{"summary":"Create one ad campaign through the ads relay. With dryRun true nothing is written. A live write is audited. Admin session only; 404 when the relay is not set up.","operationId":"postAdminAdsCampaigns","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["meta","google"]},"name":{"type":"string","minLength":1,"maxLength":120},"objective":{"type":"string","minLength":1,"maxLength":60},"dailyBudget":{"type":"integer","exclusiveMinimum":0,"maximum":10000000},"dryRun":{"type":"boolean"}},"required":["platform","name","objective","dailyBudget","dryRun"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean"},"result":{}},"required":["dryRun","result"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/ads/campaigns/{id}/status":{"post":{"summary":"Pause or resume one campaign through the ads relay. Audited. Admin session only; 404 when the relay is not set up.","operationId":"postAdminAdsCampaignsByIdStatus","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["meta","google","reddit"]},"status":{"type":"string","enum":["ACTIVE","PAUSED"]}},"required":["platform","status"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}},"required":["result"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/enrichment/run":{"post":{"summary":"Ask for one enrichment run of this site. The request waits for the daily runner on the owner's machine. A site holds one open or running request at a time (409 otherwise). Needs the content:write scope or an admin session.","operationId":"postAdminEnrichmentRun","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"site":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"limit":{"type":"integer","minimum":1,"maximum":500}},"required":["site","limit"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok","id"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/enrichment/runs":{"get":{"summary":"The enrichment import log of the site, newest first. Pass the cursor of the page before to read on. Needs the content:read scope or an admin session.","operationId":"getAdminEnrichmentRuns","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(imp_[A-Za-z0-9_]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"startedAt":{"type":"string"},"finishedAt":{"type":["string","null"]},"rowsSeen":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"rowsInserted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"rowsUpdated":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"rowsSkipped":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"issues":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"error":{"type":["string","null"]}},"required":["id","status","startedAt","finishedAt","rowsSeen","rowsInserted","rowsUpdated","rowsSkipped","issues","error"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/forms/export.csv":{"get":{"summary":"The forms inbox as CSV.","operationId":"getAdminFormsExportCsv","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"string"}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/mail":{"post":{"summary":"Mark a message (read, spam, not-spam, archive) or send a mail. Audited, without the body. Needs the mail:write scope or an admin session.","operationId":"postAdminMail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"act"},"messageId":{"type":"string","maxLength":2048,"pattern":"^[^\\s<>\\r\\n]+$"},"action":{"type":"string","enum":["read","spam","not-spam","archive"]}},"required":["kind","messageId","action"]},{"type":"object","properties":{"kind":{"type":"string","const":"send"},"to":{"type":"string","minLength":3,"maxLength":320},"subject":{"type":"string","minLength":1,"maxLength":998},"text":{"type":"string","minLength":1,"maxLength":26214400},"inReplyTo":{"anyOf":[{"type":"string","maxLength":2048,"pattern":"^[^\\s<>\\r\\n]+$"},{"type":"null"}]},"threadId":{"anyOf":[{"type":"string","maxLength":2048,"pattern":"^[^\\s<>\\r\\n]+$"},{"type":"null"}]}},"required":["kind","to","subject","text"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/mail/recent":{"get":{"summary":"The latest received mail, newest first, with the verification links and codes found in each. Returns no message body. Needs the mail:read scope or an admin session.","operationId":"getAdminMailRecent","parameters":[{"name":"to","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":320}},{"name":"since","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/tickets":{"get":{"summary":"Support tickets, oldest first. One ticket per inbound mail thread. Needs the mail:read scope or an admin session.","operationId":"getAdminSupportTickets","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","pending","resolved"]}},{"name":"escalated","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(st_[A-Za-z0-9_-]{1,160})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"threadId":{"type":"string"},"status":{"type":"string","enum":["open","pending","resolved"]},"subject":{"type":["string","null"]},"from":{"type":["string","null"]},"createdAt":{"type":"number"},"escalatedAt":{"type":["number","null"]},"escalationReason":{"type":["string","null"]}},"required":["id","threadId","status","subject","from","createdAt","escalatedAt","escalationReason"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/canned":{"get":{"summary":"The canned support replies, finished: each text is ready to send. The words come from the site config and the fixed policy sentences. Needs the mail:read scope or an admin session.","operationId":"getAdminSupportCanned","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["acknowledge","claim-listing","refund-policy","suggest-edit","takedown-received"]},"title":{"type":"string"},"text":{"type":"string"}},"required":["id","title","text"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/refund":{"post":{"summary":"Refund the payment of one subscription invoice of this site for a support ticket, in part or in full. It runs the billing refund and adds the ticket id to its audit rows. An open ticket becomes pending. A resolved ticket answers 409. Stripe is in test mode outside production. Needs the billing:write scope or an admin session.","operationId":"postAdminSupportRefund","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ticketId":{"type":"string","pattern":"^st_[A-Za-z0-9_-]{1,160}$"},"invoiceId":{"type":"string","pattern":"^in_[A-Za-z0-9_]{1,80}$"},"amount":{"type":"integer","minimum":1,"maximum":100000000},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["ticketId","invoiceId","reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"ticketId":{"type":"string"},"ticketStatus":{"type":"string","enum":["open","pending","resolved"]},"ticketUpdated":{"type":"boolean"},"localUpdated":{"type":"boolean"},"invoiceId":{"type":"string"},"chargeId":{"type":"string"},"refundId":{"type":"string"},"amount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"currency":{"type":"string"},"fullyRefunded":{"type":"boolean"},"outcome":{"type":"string"}},"required":["ok","ticketId","ticketStatus","ticketUpdated","localUpdated","invoiceId","chargeId","refundId","amount","currency","fullyRefunded","outcome"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/takedown":{"post":{"summary":"Record a takedown request and hand it to a human: the named ticket is escalated and the admins are mailed. It only records. No listing changes, no copied data is deleted and nothing is sent to the requester. Audited. Needs the mail:write scope or an admin session.","operationId":"postAdminSupportTakedown","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"requester":{"type":"string","minLength":3,"maxLength":320},"subject":{"type":"string","minLength":3,"maxLength":200},"details":{"type":"string","minLength":10,"maxLength":5000},"listing":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,200}$"},"ticketId":{"type":"string","pattern":"^st_[A-Za-z0-9_-]{1,160}$"}},"required":["requester","subject","details"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"kind":{"type":"string","enum":["takedown","legal"]},"listingId":{"type":["string","null"]},"ticketId":{"type":["string","null"]},"escalated":{"type":"boolean"},"notified":{"type":"boolean"},"note":{"type":"string","const":"A person decides. Nothing was removed or changed."}},"required":["ok","id","kind","listingId","ticketId","escalated","notified","note"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/legal":{"post":{"summary":"Record a legal request and hand it to a human: the named ticket is escalated and the admins are mailed. It only records. Nothing is changed and nothing is sent to the requester. Audited. Needs the mail:write scope or an admin session.","operationId":"postAdminSupportLegal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"requester":{"type":"string","minLength":3,"maxLength":320},"subject":{"type":"string","minLength":3,"maxLength":200},"details":{"type":"string","minLength":10,"maxLength":5000},"listing":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,200}$"},"ticketId":{"type":"string","pattern":"^st_[A-Za-z0-9_-]{1,160}$"}},"required":["requester","subject","details"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"kind":{"type":"string","enum":["takedown","legal"]},"listingId":{"type":["string","null"]},"ticketId":{"type":["string","null"]},"escalated":{"type":"boolean"},"notified":{"type":"boolean"},"note":{"type":"string","const":"A person decides. Nothing was removed or changed."}},"required":["ok","id","kind","listingId","ticketId","escalated","notified","note"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/requests":{"get":{"summary":"Takedown and legal requests, oldest first. Needs the mail:read scope or an admin session.","operationId":"getAdminSupportRequests","parameters":[{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["takedown","legal"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(sr_[A-Za-z0-9_-]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["takedown","legal"]},"ticketId":{"type":["string","null"]},"listingId":{"type":["string","null"]},"requester":{"type":"string"},"subject":{"type":"string"},"details":{"type":"string"},"createdAt":{"type":"number"}},"required":["id","kind","ticketId","listingId","requester","subject","details","createdAt"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/tickets/{id}":{"patch":{"summary":"Change one support ticket status (open, pending, resolved). Audited. Needs the mail:write scope or an admin session.","operationId":"patchAdminSupportTicketsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","pending","resolved"]},"note":{"type":"string","minLength":1,"maxLength":2000}},"required":["status"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"status":{"type":"string","enum":["open","pending","resolved"]},"previousStatus":{"type":"string","enum":["open","pending","resolved"]}},"required":["ok","id","status","previousStatus"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/tickets/{id}/escalate":{"post":{"summary":"Hand one support ticket to a human, once. Records the reason and mails the admins. Changes no status and sends nothing to the sender. Audited. Needs the mail:write scope or an admin session.","operationId":"postAdminSupportTicketsByIdEscalate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"escalatedAt":{"type":"number"},"notified":{"type":"boolean"}},"required":["ok","id","escalatedAt","notified"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/support/tickets/{id}/reply":{"post":{"summary":"Answer the sender of one support ticket by mail, on the ticket thread, with free text or the id of a canned reply (exactly one). The recipient is the verified envelope sender, never an input. An open ticket becomes pending. A resolved ticket answers 409. Audited without the body. Needs the mail:write scope or an admin session.","operationId":"postAdminSupportTicketsByIdReply","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":10000},"canned":{"type":"string","enum":["acknowledge","claim-listing","refund-policy","suggest-edit","takedown-received"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"to":{"type":"string"},"status":{"type":"string","enum":["open","pending","resolved"]},"previousStatus":{"type":"string","enum":["open","pending","resolved"]},"duplicate":{"type":"boolean"},"statusUpdated":{"type":"boolean"}},"required":["ok","id","to","status","previousStatus","duplicate","statusUpdated"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/mail/{messageId}/attachments/{attachmentId}":{"get":{"summary":"Download one attachment of a stored message.","operationId":"getAdminMailByMessageIdAttachmentsByAttachmentId","parameters":[{"name":"messageId","in":"path","required":true,"schema":{"type":"string"}},{"name":"attachmentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"string"}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/monitoring/alerts":{"get":{"summary":"The open alerts in the monitoring feed, newest first. Pass the cursor of the page before to read on.","operationId":"getAdminMonitoringAlerts","parameters":[{"name":"site","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["uptime","error","queue","cost"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","resolved","all"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.(mev_[a-z0-9_]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/monitoring/sites":{"get":{"summary":"The site rollup: identity, the newest event of each kind and the open-alert counts.","operationId":"getAdminMonitoringSites","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"sites":{"type":"array","items":{}}},"required":["sites"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/domains/{domain}/status":{"get":{"summary":"The domain, DNS, routing and mailbox state. Needs the domains:manage scope or an admin session.","operationId":"getAdminDomainsByDomainStatus","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/domains/{domain}/verify":{"post":{"summary":"Re-read the Cloudflare state and cross-check it against a public resolver, one verdict per check. Audited. Needs the domains:manage scope or an admin session.","operationId":"postAdminDomainsByDomainVerify","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/leads/dispatch":{"post":{"summary":"Start one parallel call run. Needs the leads:write scope or an admin session.","operationId":"postAdminLeadsDispatch","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","enum":["telnyx","bland","vapi"]},"limit":{"type":"integer","minimum":1,"maximum":100},"leadIds":{"maxItems":100,"type":"array","items":{"type":"string","minLength":1}}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"requested":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"started":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"results":{"type":"array","items":{}}},"required":["ok","requested","started","results"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/leads/drips":{"post":{"summary":"Create, update or delete a drip sequence: a name plus ordered steps (template, delay in days; 0 sends at enroll time). A step's channel is its template's kind; a mismatch is refused, and a delete is refused while an enrollment is active. Also enroll a lead (dripId, leadId, optional postcardTo snapshot; a second active enrollment is 409) or stop an enrollment (id). Needs the leads:write scope or an admin session.","operationId":"postAdminLeadsDrips","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"create"},"name":{"type":"string","minLength":1,"maxLength":120},"steps":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"object","properties":{"templateId":{"type":"string","minLength":1,"maxLength":64},"channel":{"type":"string","enum":["text","call","postcard"]},"delayDays":{"type":"integer","minimum":0,"maximum":365}},"required":["templateId","channel","delayDays"]}}},"required":["action","name","steps"]},{"type":"object","properties":{"action":{"type":"string","const":"update"},"id":{"type":"string","minLength":1,"maxLength":64},"name":{"type":"string","minLength":1,"maxLength":120},"steps":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"object","properties":{"templateId":{"type":"string","minLength":1,"maxLength":64},"channel":{"type":"string","enum":["text","call","postcard"]},"delayDays":{"type":"integer","minimum":0,"maximum":365}},"required":["templateId","channel","delayDays"]}}},"required":["action","id","name","steps"]},{"type":"object","properties":{"action":{"type":"string","const":"delete"},"id":{"type":"string","minLength":1,"maxLength":64}},"required":["action","id"]},{"type":"object","properties":{"action":{"type":"string","const":"enroll"},"dripId":{"type":"string","minLength":1,"maxLength":64},"leadId":{"type":"string","minLength":1,"maxLength":64},"postcardTo":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"line1":{"type":"string","minLength":1,"maxLength":120},"line2":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"city":{"type":"string","minLength":1,"maxLength":80},"state":{"type":"string","minLength":2,"maxLength":2},"zip":{"type":"string","pattern":"^\\d{5}(-\\d{4})?$"}},"required":["name","line1","city","state","zip"]}},"required":["action","dripId","leadId"]},{"type":"object","properties":{"action":{"type":"string","const":"stop"},"id":{"type":"string","minLength":1,"maxLength":64}},"required":["action","id"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/leads/{id}":{"post":{"summary":"Set lead status, request a call, set the script, send a text or a postcard, refresh a transcript or barge in. Needs the leads:write scope or an admin session.","operationId":"postAdminLeadsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"status"},"status":{"type":"string","enum":["new","delivered","responded","spam","refunded"]}},"required":["action","status"]},{"type":"object","properties":{"action":{"type":"string","const":"request-call"},"provider":{"type":"string","minLength":1,"maxLength":16},"scriptId":{"type":"string","minLength":1,"maxLength":64}},"required":["action","provider"]},{"type":"object","properties":{"action":{"type":"string","const":"set-script"},"scriptId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]}},"required":["action","scriptId"]},{"type":"object","properties":{"action":{"type":"string","const":"text"},"body":{"type":"string","minLength":1,"maxLength":600}},"required":["action","body"]},{"type":"object","properties":{"action":{"type":"string","const":"postcard"},"to":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"line1":{"type":"string","minLength":1,"maxLength":120},"line2":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"city":{"type":"string","minLength":1,"maxLength":80},"state":{"type":"string","minLength":2,"maxLength":2},"zip":{"type":"string","pattern":"^\\d{5}(-\\d{4})?$"}},"required":["name","line1","city","state","zip"]},"message":{"type":"string","minLength":1,"maxLength":500}},"required":["action","to","message"]},{"type":"object","properties":{"action":{"type":"string","const":"transcript"},"callId":{"type":"string","minLength":1,"maxLength":64}},"required":["action","callId"]},{"type":"object","properties":{"action":{"type":"string","const":"barge-in"},"callId":{"type":"string","minLength":1,"maxLength":64}},"required":["action","callId"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":{}}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/leads/kb":{"post":{"summary":"Create or delete a knowledge-base document. Needs the leads:write scope or an admin session.","operationId":"postAdminLeadsKb","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"create"},"kind":{"type":"string","enum":["text","faq"]},"title":{"type":"string","minLength":1,"maxLength":200},"question":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"body":{"type":"string","minLength":1,"maxLength":8000},"scriptIds":{"maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["action","kind","title","body"]},{"type":"object","properties":{"action":{"type":"string","const":"delete"},"id":{"type":"string","minLength":1,"maxLength":64}},"required":["action","id"]}]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/leads/reconcile":{"get":{"summary":"Billed lead charges plus call spend, per listing.","operationId":"getAdminLeadsReconcile","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/leads/scripts":{"post":{"summary":"Create, update or delete a call script. Needs the leads:write scope or an admin session.","operationId":"postAdminLeadsScripts","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"create"},"name":{"type":"string","minLength":1,"maxLength":120},"systemPrompt":{"type":"string","minLength":1,"maxLength":4000},"personality":{"type":"string","minLength":1,"maxLength":200},"firstMessage":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"voiceBland":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"voiceTelnyx":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"voiceVapi":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"maxDurationSec":{"type":"integer","minimum":10,"maximum":7200},"bargeIn":{"type":"boolean"},"isDefault":{"type":"boolean"},"kbDocIds":{"maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["action","name","systemPrompt","personality"]},{"type":"object","properties":{"action":{"type":"string","const":"update"},"id":{"type":"string","minLength":1,"maxLength":64},"name":{"type":"string","minLength":1,"maxLength":120},"systemPrompt":{"type":"string","minLength":1,"maxLength":4000},"personality":{"type":"string","minLength":1,"maxLength":200},"firstMessage":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"voiceBland":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"voiceTelnyx":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"voiceVapi":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"maxDurationSec":{"type":"integer","minimum":10,"maximum":7200},"bargeIn":{"type":"boolean"},"isDefault":{"type":"boolean"},"kbDocIds":{"maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["action","id","name","systemPrompt","personality"]},{"type":"object","properties":{"action":{"type":"string","const":"delete"},"id":{"type":"string","minLength":1,"maxLength":64}},"required":["action","id"]}]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/leads/templates":{"post":{"summary":"Create, update or delete a message template (text, call or postcard body with {{variables}}). Needs the leads:write scope or an admin session.","operationId":"postAdminLeadsTemplates","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"create"},"kind":{"type":"string","enum":["text","call","postcard"]},"name":{"type":"string","minLength":1,"maxLength":120},"body":{"type":"string","minLength":1,"maxLength":8000}},"required":["action","kind","name","body"]},{"type":"object","properties":{"action":{"type":"string","const":"update"},"id":{"type":"string","minLength":1,"maxLength":64},"kind":{"type":"string","enum":["text","call","postcard"]},"name":{"type":"string","minLength":1,"maxLength":120},"body":{"type":"string","minLength":1,"maxLength":8000}},"required":["action","id","kind","name","body"]},{"type":"object","properties":{"action":{"type":"string","const":"delete"},"id":{"type":"string","minLength":1,"maxLength":64}},"required":["action","id"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/link-exchanges/{id}":{"post":{"summary":"Approve, reject, spam or recheck a link exchange.","operationId":"postAdminLinkExchangesById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"approve"},"note":{"type":"string","maxLength":500},"guideSlug":{"type":"string","maxLength":120}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"reject"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","reason"]},{"type":"object","properties":{"action":{"type":"string","const":"spam"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","reason"]},{"type":"object","properties":{"action":{"type":"string","const":"recheck"}},"required":["action"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings":{"get":{"summary":"List listings of every status (draft, pending, published, archived), ordered by id. Filter by status, category slug and place path. Pass nextCursor as cursor to read on. Needs the listings:read scope or an admin session.","operationId":"getAdminListings","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","pending","published","archived"]}},{"name":"category","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":120}},{"name":"place","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z]{2}(\\/[a-z0-9-]+){0,2}$"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["draft","pending","published","archived"]},"tier":{"type":"string"},"city":{"type":"string"},"region":{"type":"string"},"country":{"type":"string"},"completeness":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updatedAt":{"type":"string"}},"required":["id","slug","name","status","tier","city","region","country","completeness","updatedAt"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}":{"get":{"summary":"Read one listing of any status by id or slug: every field, attributes, categories (primary first), and its photo and review counts. Needs the listings:read scope or an admin session.","operationId":"getAdminListingsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["draft","pending","published","archived"]},"tier":{"type":"string"},"city":{"type":"string"},"region":{"type":"string"},"country":{"type":"string"},"completeness":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updatedAt":{"type":"string"},"description":{"type":["string","null"]},"street":{"type":["string","null"]},"street2":{"type":["string","null"]},"regionCode":{"type":["string","null"]},"postalCode":{"type":["string","null"]},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"email":{"type":["string","null"]},"currency":{"type":"string"},"language":{"type":"string"},"sourceRef":{"type":["string","null"]},"sourceUrl":{"type":["string","null"]},"source":{"type":["string","null"]},"claimedBy":{"type":["string","null"]},"attributes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"categories":{"type":"array","items":{"type":"string"}},"photos":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"reviews":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string"},"publishedAt":{"type":["string","null"]}},"required":["id","slug","name","status","tier","city","region","country","completeness","updatedAt","description","street","street2","regionCode","postalCode","lat","lng","phone","website","email","currency","language","sourceRef","sourceUrl","source","claimedBy","attributes","categories","photos","reviews","createdAt","publishedAt"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Preview deletion by default, with per-table row counts in the plan. Apply with body dryRun false and confirm equal to the listing id. An owner, an open claim, an open checkout, a live or incomplete subscription, a held or active booking, or a waitlist entry returns 409. Failed object cleanup returns orphanedObjects and retries through the upload cron. Stripe is never called. Needs listings:write or an admin session.","operationId":"deleteAdminListingsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"default":true,"type":"boolean"},"confirm":{"type":"string","maxLength":80}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"slug":{"type":"string"},"dryRun":{"type":"boolean"},"applied":{"type":"boolean"},"counts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"photoKeys":{"type":"array","items":{"type":"string"}},"objectKeys":{"type":"array","items":{"type":"string"}},"orphanedObjects":{"type":"array","items":{"type":"string"}}},"required":["ok","id","slug","dryRun","applied","counts","photoKeys","objectKeys","orphanedObjects"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Change some fields of one listing, by id or slug: editable import fields, categories (the first is primary), attributes, status and place. A field not named keeps its value; null clears nullable fields. Source identity is read-only; currency, language and source_url reject null. Set status archived to take a listing down and keep it. Public pages show edits within about 10 minutes. Needs the listings:write scope or an admin session.","operationId":"patchAdminListingsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":300},"description":{"anyOf":[{"type":"string","maxLength":20000},{"type":"null"}]},"categories":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":120}},"status":{"type":"string","enum":["draft","pending","published","archived"]},"street":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"street2":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"city":{"type":"string","minLength":1,"maxLength":200},"region":{"type":"string","minLength":1,"maxLength":200},"region_code":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}]},"postal_code":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}]},"country":{"type":"string","minLength":1,"maxLength":100},"lat":{"anyOf":[{"type":"number","minimum":-90,"maximum":90},{"type":"null"}]},"lng":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"null"}]},"phone":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"website":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"email":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}]},"currency":{"type":"string","maxLength":3},"language":{"type":"string","maxLength":35},"source_url":{"type":"string","maxLength":2000},"attributes":{"type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-zA-Z0-9_]*$"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"},{"type":"array","items":{"type":["string","number","boolean"]}}]}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"slug":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","slug","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Create or replace one listing at its slug. The importer checks it as it checks `bun run import`. Omitted status and omitted or empty categories keep their stored values. Existing source identity is fixed. A different non-empty source_ref returns 400; omitted, null or empty values retain both source fields. Create a new listing to use a different identity. New listings accept source_ref; a configured prefix selects a source. Omitted source_url resets to the configured URL. Other omitted fields use defaults. Tier is never set here. Public pages show edits within about 10 minutes. Needs the listings:write scope or an admin session.","operationId":"putAdminListingsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":300},"description":{"anyOf":[{"type":"string","maxLength":20000},{"type":"null"}]},"categories":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":120}},"status":{"type":"string","enum":["draft","pending","published","archived"]},"street":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"street2":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"city":{"type":"string","minLength":1,"maxLength":200},"region":{"type":"string","minLength":1,"maxLength":200},"region_code":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}]},"postal_code":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}]},"country":{"type":"string","minLength":1,"maxLength":100},"lat":{"anyOf":[{"type":"number","minimum":-90,"maximum":90},{"type":"null"}]},"lng":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"null"}]},"phone":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"website":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"email":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}]},"currency":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}]},"language":{"anyOf":[{"type":"string","maxLength":35},{"type":"null"}]},"source_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"attributes":{"type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-zA-Z0-9_]*$"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"},{"type":"array","items":{"type":["string","number","boolean"]}}]}},"source_ref":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"required":["name","city","region","country"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"slug":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","slug","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Feature, unfeature or mark a listing real.","operationId":"postAdminListingsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["feature","unfeature","mark-real"]},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"listingId":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/bulk":{"post":{"summary":"Validate up to 100 listing rows and return the plan with field diffs (dryRun, the default), or apply it in one batch. Needs the listings:write scope or an admin session.","operationId":"postAdminListingsBulk","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"},{"type":"array","items":{"type":["string","number","boolean"]}}]}}},"source":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},"dryRun":{"default":true,"type":"boolean"}},"required":["rows"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"applied":{"type":"boolean"},"counts":{"type":"object","properties":{"rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"added":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updated":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unchanged":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"duplicates":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"places":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["rows","added","updated","unchanged","duplicates","places"],"additionalProperties":false},"rows":{"type":"array","items":{"type":"object","properties":{"line":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"id":{"type":"string"},"slug":{"type":"string"},"action":{"type":"string","enum":["added","updated","unchanged"]},"changes":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"from":{},"to":{}},"required":["field","from","to"],"additionalProperties":false}}},"required":["line","id","slug","action","changes"],"additionalProperties":false}},"warnings":{"type":"array","items":{"type":"object","properties":{"line":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"message":{"type":"string"}},"required":["message"],"additionalProperties":false}}},"required":["ok","dryRun","applied","counts","rows","warnings"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/photos":{"get":{"summary":"Uploaded photos of a listing, in display order. Needs the content:read scope or an admin session.","operationId":"getAdminListingsBySlugPhotos","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"contentType":{"type":["string","null"]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"sizeBytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"alt":{"type":["string","null"]},"sortOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","url","contentType","width","height","sizeBytes","alt","sortOrder"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Add an upload to a listing: a multipart body with a file field (and alt), or JSON with an https url the server fetches. JPEG, PNG or WebP only; the size and pixel limits of owner uploads apply. Needs the listings:write scope or an admin session.","operationId":"postAdminListingsBySlugPhotos","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":2000,"format":"uri","description":"An https image the server fetches. A multipart upload with a file field, and an optional alt field, takes its place."},"alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"photo":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"contentType":{"type":["string","null"]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"sizeBytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"alt":{"type":["string","null"]},"sortOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","url","contentType","width","height","sizeBytes","alt","sortOrder"],"additionalProperties":false}},"required":["ok","photo"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Put the photos in a new order in one call. Name every uploaded photo of the listing. Needs the listings:write scope or an admin session.","operationId":"patchAdminListingsBySlugPhotos","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"order":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1},"description":"Photo ids in the new order. Name every uploaded photo of the listing."}},"required":["order"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"order":{"type":"array","items":{"type":"string"}}},"required":["ok","order"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/photos/{photoId}":{"patch":{"summary":"Change the alt text or the sort order of one photo. Needs the listings:write scope or an admin session.","operationId":"patchAdminListingsBySlugPhotosByPhotoId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"photoId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"sortOrder":{"type":"integer","minimum":0,"maximum":10000}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete one uploaded photo and its R2 object. Failed object deletions remain queued for retry. Needs the listings:write scope or an admin session.","operationId":"deleteAdminListingsBySlugPhotosByPhotoId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"photoId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"objectDeleted":{"type":"boolean"}},"required":["ok","objectDeleted"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/source-photos":{"get":{"summary":"List source photos in display order, including hidden rows. Needs content:read or an admin session.","operationId":"getAdminListingsBySlugSourcePhotos","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{},"items":{"type":"array","items":{}}},"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Reorder all source photos, including hidden rows. Name every id exactly once. Dry run by default; set dryRun false to apply. Needs listings:write or an admin session.","operationId":"patchAdminListingsBySlugSourcePhotos","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"order":{"minItems":1,"maxItems":12,"type":"array","items":{"type":"string","minLength":1}},"dryRun":{"default":true,"type":"boolean"}},"required":["order"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{},"items":{"type":"array","items":{}}},"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/source-photos/{rowId}":{"get":{"summary":"Read one source photo on this listing. Needs content:read or an admin session.","operationId":"getAdminListingsBySlugSourcePhotosByRowId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"rowId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{},"items":{"type":"array","items":{}}},"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Edit source photo alt text. Dry run by default; set dryRun false to apply. Imports never replace stored photos. Needs listings:write or an admin session.","operationId":"patchAdminListingsBySlugSourcePhotosByRowId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"rowId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"alt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"dryRun":{"default":true,"type":"boolean"}},"required":["alt"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{},"items":{"type":"array","items":{}}},"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Suppress a source photo and retain its identity. Dry run by default. Apply with dryRun false and confirm equal to rowId. Imports cannot restore it. Needs listings:write or an admin session.","operationId":"deleteAdminListingsBySlugSourcePhotosByRowId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"rowId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"default":true,"type":"boolean"},"confirm":{"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{},"items":{"type":"array","items":{}}},"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/source-reviews":{"get":{"summary":"All copied platform reviews of one listing, hidden ones too. Needs the listings:write scope or an admin session.","operationId":"getAdminListingsBySlugSourceReviews","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Add one platform review to a listing. Dry run unless confirm=true. The row is locked, so an enrichment run never overwrites it. Needs the listings:write scope or an admin session.","operationId":"postAdminListingsBySlugSourceReviews","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"confirm","in":"query","required":false,"description":"Send true to apply. Without it the call is a dry run.","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["google","venue","yelp","tripadvisor","wikimedia","github","mapillary"]},"author":{"anyOf":[{"type":"string","minLength":1,"maxLength":120},{"type":"null"}]},"body":{"type":"string","minLength":1,"maxLength":2000},"permalink":{"type":"string","maxLength":500,"format":"uri"},"sourceUrl":{"type":"string","maxLength":500,"format":"uri"},"truncated":{"type":"boolean"},"date":{"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))$","description":"When the platform showed it (ISO 8601). Default: now."},"hidden":{"type":"boolean"}},"required":["platform","body","permalink"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/source-reviews/{rowId}":{"patch":{"summary":"Edit or hide one review. Dry run unless confirm=true. A content edit locks the row against enrichment runs. hidden removes it from the page and keeps it in the table, so a run does not add it back. Needs the listings:write scope or an admin session.","operationId":"patchAdminListingsBySlugSourceReviewsByRowId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"rowId","in":"path","required":true,"schema":{"type":"string"}},{"name":"confirm","in":"query","required":false,"description":"Send true to apply. Without it the call is a dry run.","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["google","venue","yelp","tripadvisor","wikimedia","github","mapillary"]},"author":{"anyOf":[{"type":"string","minLength":1,"maxLength":120},{"type":"null"}]},"body":{"type":"string","minLength":1,"maxLength":2000},"sourceUrl":{"type":"string","maxLength":500,"format":"uri"},"truncated":{"type":"boolean"},"date":{"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))$","description":"When the platform showed it (ISO 8601). Default: now."},"hidden":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Remove one review from public pages. Dry run unless confirm=true. Retain its hidden, admin-locked identity so imports cannot re-add it. Needs the listings:write scope or an admin session.","operationId":"deleteAdminListingsBySlugSourceReviewsByRowId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"rowId","in":"path","required":true,"schema":{"type":"string"}},{"name":"confirm","in":"query","required":false,"description":"Send true to apply. Without it the call is a dry run.","schema":{"type":"string","enum":["true"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/source-ratings":{"get":{"summary":"All copied platform ratings of one listing, hidden ones too. Needs the listings:write scope or an admin session.","operationId":"getAdminListingsBySlugSourceRatings","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Add one platform rating to a listing. Dry run unless confirm=true. The row is locked, so an enrichment run never overwrites it. Needs the listings:write scope or an admin session.","operationId":"postAdminListingsBySlugSourceRatings","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"confirm","in":"query","required":false,"description":"Send true to apply. Without it the call is a dry run.","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["google","venue","yelp","tripadvisor","wikimedia","github","mapillary"]},"rating":{"type":"number","minimum":0,"maximum":5},"count":{"type":"integer","minimum":0,"maximum":100000000},"sourceUrl":{"type":"string","maxLength":500,"format":"uri"},"permalink":{"type":"string","maxLength":500,"format":"uri"},"date":{"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))$","description":"When the platform showed it (ISO 8601). Default: now."},"hidden":{"type":"boolean"}},"required":["platform","rating","count","sourceUrl"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/listings/{slug}/source-ratings/{rowId}":{"patch":{"summary":"Edit or hide one rating. Dry run unless confirm=true. A content edit locks the row against enrichment runs. hidden removes it from the page and keeps it in the table, so a run does not add it back. Needs the listings:write scope or an admin session.","operationId":"patchAdminListingsBySlugSourceRatingsByRowId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"rowId","in":"path","required":true,"schema":{"type":"string"}},{"name":"confirm","in":"query","required":false,"description":"Send true to apply. Without it the call is a dry run.","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rating":{"type":"number","minimum":0,"maximum":5},"count":{"type":"integer","minimum":0,"maximum":100000000},"sourceUrl":{"type":"string","maxLength":500,"format":"uri"},"permalink":{"anyOf":[{"type":"string","maxLength":500,"format":"uri"},{"type":"null"}]},"date":{"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))$","description":"When the platform showed it (ISO 8601). Default: now."},"hidden":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Remove one rating from public pages. Dry run unless confirm=true. Retain its hidden, admin-locked identity so imports cannot re-add it. Needs the listings:write scope or an admin session.","operationId":"deleteAdminListingsBySlugSourceRatingsByRowId","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"rowId","in":"path","required":true,"schema":{"type":"string"}},{"name":"confirm","in":"query","required":false,"description":"Send true to apply. Without it the call is a dry run.","schema":{"type":"string","enum":["true"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"dryRun":{"type":"boolean"},"item":{}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/moderation/queue":{"get":{"summary":"Every pending submission, edit suggestion, manual claim, open report and pending link exchange as one list, oldest first. Needs the submissions:review scope or an admin session.","operationId":"getAdminModerationQueue","parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["submission","edit","claim","report","link-exchange"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^(\\d{1,15})\\.([a-z]{3}_[A-Za-z0-9_]{1,60})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/moderation/{id}":{"patch":{"summary":"Decide one queue item. The id prefix (sub_, edt_, clm_, rpt_, lnk_) names its type. Needs the submissions:review scope or an admin session.","operationId":"patchAdminModerationById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["approve","reject","spam"]},"note":{"type":"string","maxLength":500},"reason":{"type":"string","minLength":3,"maxLength":500},"currentOwnerId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]}},"required":["action"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"type":{"type":"string","enum":["submission","edit","claim","report","link-exchange"]},"status":{"type":"string"},"listingId":{"type":"string"},"url":{"type":"string"}},"required":["ok","id","type","status"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/moderation/ban":{"get":{"summary":"The bans, newest first. An IP ban shows the hash. Needs the submissions:review scope or an admin session.","operationId":"getAdminModerationBan","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Ban an email address, an IP or the sender of a queue item. Needs the submissions:review scope or an admin session.","operationId":"postAdminModerationBan","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":3,"maxLength":500},"email":{"type":"string","maxLength":320,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"ip":{"type":"string","minLength":3,"maxLength":64},"itemId":{"type":"string","minLength":1,"maxLength":64}},"required":["reason"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["email","ip"]}},"required":["id","kind"],"additionalProperties":false}}},"required":["ok","items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Lift one ban by its id. Needs the submissions:review scope or an admin session.","operationId":"deleteAdminModerationBan","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64}},"required":["id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/newsletter/api-keys":{"get":{"summary":"The newsletter API keys. Needs the newsletter:write scope or an admin session.","operationId":"getAdminNewsletterApiKeys","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create or revoke a newsletter API key. Audited. The secret returns once, in the create answer. Needs the newsletter:write scope or an admin session.","operationId":"postAdminNewsletterApiKeys","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"newsletterId":{"type":"string","minLength":1},"action":{"type":"string","const":"revoke"},"id":{"type":"string","minLength":1}}}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/newsletter/contacts":{"get":{"summary":"A page of subscribers, or the filtered CSV export with format=csv.","operationId":"getAdminNewsletterContacts","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Add, change or remove one subscriber, mail the confirmation to selected pending subscribers, or import a CSV file of subscribers.","operationId":"postAdminNewsletterContacts","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"nl":{"type":"string","minLength":1,"maxLength":64},"id":{"type":"string","minLength":1,"maxLength":64},"email":{"type":"string","minLength":1,"maxLength":320},"consent":{"type":"boolean"},"status":{"type":"string","enum":["pending","active","unsubscribed","bounced","complained"]},"reason":{"type":"string","maxLength":500},"remove":{"type":"boolean"},"sendConfirmation":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/newsletter/campaigns":{"get":{"summary":"The campaigns, newest first, without bodies. Needs the newsletter:write scope or an admin session.","operationId":"getAdminNewsletterCampaigns","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create a campaign draft. Needs the newsletter:write scope or an admin session.","operationId":"postAdminNewsletterCampaigns","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","maxLength":150},"preheader":{"type":"string","maxLength":150},"bodyJson":{"type":"string","maxLength":200000},"bodyHtml":{"type":"string","maxLength":200000}}}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"updatedAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","updatedAt"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/newsletter/campaigns/{id}":{"get":{"summary":"One campaign with its body. Needs the newsletter:write scope or an admin session.","operationId":"getAdminNewsletterCampaignsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Save a draft or scheduled campaign body. Needs the newsletter:write scope or an admin session.","operationId":"patchAdminNewsletterCampaignsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","maxLength":150},"preheader":{"type":"string","maxLength":150},"bodyJson":{"type":"string","maxLength":200000},"bodyHtml":{"type":"string","maxLength":200000},"baseUpdatedAt":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["baseUpdatedAt"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"updatedAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["updatedAt"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete a campaign. Needs the newsletter:write scope or an admin session.","operationId":"deleteAdminNewsletterCampaignsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Schedule, cancel, queue, duplicate or test-send a campaign. Needs the newsletter:write scope or an admin session.","operationId":"postAdminNewsletterCampaignsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"schedule"},"scheduledAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["action","scheduledAt"]},{"type":"object","properties":{"action":{"type":"string","const":"cancel"}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"queue"}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"duplicate"}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"test-send"},"addresses":{"minItems":1,"maxItems":5,"type":"array","items":{"type":"string","minLength":3,"maxLength":254}}},"required":["action","addresses"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":{}}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/reports/leads":{"get":{"summary":"Lead counts by status and cost per lead by channel (call, text, postcard) and currency for a period. Needs the reports:read scope or an admin session.","operationId":"getAdminReportsLeads","parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"string"}},{"name":"until","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"site":{"type":"object","properties":{"id":{"type":"string"},"domain":{"type":"string"}},"required":["id","domain"],"additionalProperties":false},"period":{"type":"object","properties":{"since":{"type":"string"},"until":{"type":"string"}},"required":["since","until"],"additionalProperties":false},"leads":{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"byStatus":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}}},"required":["total","byStatus"],"additionalProperties":false},"channels":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string"},"currency":{"type":"string"},"sends":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"leadsReached":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"costCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"costPerLeadCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["channel","currency","sends","leadsReached","costCents","costPerLeadCents"],"additionalProperties":false}}},"required":["site","period","leads","channels"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/reports/revenue":{"get":{"summary":"Gross, refunded and net revenue per currency for a period, from Stripe events and the store, plus active subscriptions by tier. Needs the reports:read scope or an admin session.","operationId":"getAdminReportsRevenue","parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"string"}},{"name":"until","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"site":{"type":"object","properties":{"id":{"type":"string"},"domain":{"type":"string"}},"required":["id","domain"],"additionalProperties":false},"period":{"type":"object","properties":{"since":{"type":"string"},"until":{"type":"string"}},"required":["since","until"],"additionalProperties":false},"livemode":{"type":"boolean"},"currencies":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"invoicesPaidCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"storePaidCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"refundedCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"netCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["currency","invoicesPaidCents","storePaidCents","refundedCents","netCents"],"additionalProperties":false}},"activeSubscriptions":{"type":"array","items":{"type":"object","properties":{"tier":{"type":"string"},"kind":{"type":"string"},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["tier","kind","count"],"additionalProperties":false}},"leadChargesBilled":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["currency","cents"],"additionalProperties":false}}},"required":["site","period","livemode","currencies","activeSubscriptions","leadChargesBilled"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/reports/traffic":{"get":{"summary":"Search Console clicks, impressions, CTR and position for a period, with the top pages and queries. With no Search Console token it answers configured:false and no numbers. A Search Console fault answers 502. Needs the reports:read scope or an admin session.","operationId":"getAdminReportsTraffic","parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"string"}},{"name":"until","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"site":{"type":"object","properties":{"id":{"type":"string"},"domain":{"type":"string"}},"required":["id","domain"],"additionalProperties":false},"period":{"type":"object","properties":{"since":{"type":"string"},"until":{"type":"string"}},"required":["since","until"],"additionalProperties":false},"configured":{"type":"boolean"},"property":{"type":"string"},"totals":{"anyOf":[{"type":"object","properties":{"clicks":{"type":"number"},"impressions":{"type":"number"},"ctr":{"type":"number"},"position":{"type":["number","null"]}},"required":["clicks","impressions","ctr","position"],"additionalProperties":false},{"type":"null"}]},"pages":{"type":"array","items":{"type":"object","properties":{"page":{"type":"string"},"clicks":{"type":"number"},"impressions":{"type":"number"},"ctr":{"type":"number"},"position":{"type":"number"}},"required":["page","clicks","impressions","ctr","position"],"additionalProperties":false}},"queries":{"type":"array","items":{"type":"object","properties":{"query":{"type":"string"},"clicks":{"type":"number"},"impressions":{"type":"number"},"ctr":{"type":"number"},"position":{"type":"number"}},"required":["query","clicks","impressions","ctr","position"],"additionalProperties":false}}},"required":["site","period","configured","property","totals","pages","queries"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/seo/audit":{"get":{"summary":"The index audit of the site (robots, sitemap, sample pages) plus the sitemap status Search Console reports.","operationId":"getAdminSeoAudit","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/seo/indexnow":{"post":{"summary":"Push the listings and guides that changed to IndexNow now. Audited. Production only.","operationId":"postAdminSeoIndexnow","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/seo/resubmit-sitemap":{"post":{"summary":"Submit the sitemap to IndexNow and add it to Search Console. Audited. Production only.","operationId":"postAdminSeoResubmitSitemap","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{}},"required":["data"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/settings":{"get":{"summary":"The site settings.","operationId":"getAdminSettings","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Create or update one setting.","operationId":"putAdminSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z0-9_.-]{1,120}$"},"value":{}},"required":["key","value"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Remove one setting.","operationId":"deleteAdminSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","minLength":1}},"required":["key"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/revalidate":{"post":{"summary":"Expire the cached public reads (listings, sources) so the next read sees a write that bypassed the API, such as a CLI import. Audited. Needs the listings:write scope or an admin session.","operationId":"postAdminRevalidate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"minItems":1,"maxItems":3,"type":"array","items":{"type":"string","enum":["listings","sources","products"]}},"all":{"type":"boolean","const":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string","enum":["listings","sources","products"]}}},"required":["ok","tags"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/sources/{key}":{"put":{"summary":"Create or replace one import source. Needs the sources:write scope or an admin session.","operationId":"putAdminSourcesByKey","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"licenseUrl":{"anyOf":[{"type":"string","maxLength":2000,"format":"uri"},{"type":"null"}]},"attributionRequired":{"default":true,"type":"boolean"},"name":{"type":"string","minLength":1,"maxLength":120},"url":{"type":"string","maxLength":2000,"format":"uri"},"license":{"type":"string","minLength":1,"maxLength":200},"refresh":{"default":"manual","type":"string","enum":["manual","daily","weekly","monthly","quarterly","yearly"]},"notes":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["name","url","license"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete one source that has no listings and no import runs. Needs the sources:write scope or an admin session.","operationId":"deleteAdminSourcesByKey","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/submissions":{"get":{"summary":"The submission queue, newest first.","operationId":"getAdminSubmissions","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/submissions/{id}":{"post":{"summary":"Approve, reject, spam or merge a submission.","operationId":"postAdminSubmissionsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"approve"},"note":{"type":"string","maxLength":500}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"reject"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","reason"]},{"type":"object","properties":{"action":{"type":"string","const":"spam"},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","reason"]},{"type":"object","properties":{"action":{"type":"string","const":"merge"},"listingId":{"type":"string","minLength":1,"maxLength":64},"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["action","listingId","reason"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"listingId":{"type":"string"},"url":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/tokens":{"get":{"summary":"The service tokens, with hints and metadata only.","operationId":"getAdminTokens","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Issue a service token. The secret comes back in this response only.","operationId":"postAdminTokens","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"scopes":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","enum":["sites:provision","listings:read","listings:write","apps:write","categories:write","places:write","sources:write","settings:write","leads:write","crm:write","newsletter:write","submissions:review","billing:read","billing:write","audit:read","reports:read","content:read","content:write","agent:run","tokens:manage","seo:write","users:manage","mail:read","mail:write","video:write","domains:manage","outreach:read","outreach:write","products:write","academy:read","academy:write"]}},"ttlDays":{"default":30,"type":"integer","minimum":1,"maximum":365}},"required":["name","scopes"]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"prefix":{"type":"string"},"token":{"type":"string"}},"required":["id","prefix","token"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/tokens/{id}":{"delete":{"summary":"Revoke a service token.","operationId":"deleteAdminTokensById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/outreach/messages/{id}":{"post":{"summary":"Edit, approve, cancel or send one outreach email message. Send is refused (409, with a code) unless every rule in docs/ops/outreach.md passes. Session only.","operationId":"postAdminOutreachMessagesById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"edit"},"subject":{"type":"string","minLength":1,"maxLength":200,"pattern":"^[^\\r\\n]+$"},"body":{"type":"string","minLength":20,"maxLength":5000}},"required":["action","subject","body"]},{"type":"object","properties":{"action":{"type":"string","const":"approve"}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"cancel"}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"send"}},"required":["action"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/outreach/reconcile":{"post":{"summary":"Read the mail inbox for replies and bounces that name a sent outreach message. A reply ends the sequence. A bounce suppresses the address and counts against the sender. Session only.","operationId":"postAdminOutreachReconcile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"replied":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bounced":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"paused":{"type":["string","null"]}},"required":["ok","replied","bounced","paused"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/outreach/sender":{"put":{"summary":"Set the outreach sender: name, mailbox, postal address, DKIM selector, warm-up start, cap override and pause. Session only.","operationId":"putAdminOutreachSender","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fromName":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[^\\r\\n]+$"},"fromEmail":{"type":"string","maxLength":254,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"physicalAddress":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"dkimSelector":{"type":"string","pattern":"^[a-z0-9_-]{1,63}$"},"warmupStartedAt":{"anyOf":[{"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))$"},{"type":"null"}]},"dailyCapOverride":{"anyOf":[{"type":"integer","minimum":0,"maximum":40},{"type":"null"}]},"paused":{"type":"boolean"}},"required":["fromName","fromEmail","physicalAddress","dkimSelector"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok","id"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/outreach/suppressions":{"post":{"summary":"Add or lift one outreach suppression. An add closes the targets it covers. Session only.","operationId":"postAdminOutreachSuppressions","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"add"},"kind":{"type":"string","enum":["email","domain"]},"value":{"type":"string","minLength":3,"maxLength":254}},"required":["action","kind","value"]},{"type":"object","properties":{"action":{"type":"string","const":"remove"},"id":{"type":"string","minLength":1}},"required":["action","id"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"closed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/outreach/campaigns":{"get":{"summary":"Outreach campaigns over the CRM, newest first, 50 a page (limit up to 100). Filter by status and channel; page with the cursor. Needs the outreach:read scope or an admin session.","operationId":"getAdminOutreachCampaigns","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","sending","done","cancelled"]}},{"name":"channel","in":"query","required":false,"schema":{"type":"string","enum":["call","email","text","postcard"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{1,15}\\.cmp_[A-Za-z0-9_-]{1,160}$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"channel":{"type":"string","enum":["call","email","text","postcard"]},"status":{"type":"string","enum":["draft","sending","done","cancelled"]},"audience":{"type":"object","properties":{"status":{"type":"string"},"tag":{"type":"string"},"source":{"type":"string"}},"additionalProperties":false},"subject":{"type":["string","null"]},"body":{"type":"string"},"spendCapCents":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"unitCostCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"number"},"updatedAt":{"type":"number"}},"required":["id","name","channel","status","audience","subject","body","spendCapCents","unitCostCents","createdAt","updatedAt"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create one draft outreach campaign: a name, a channel (call, email, text or postcard), a saved CRM audience (status, tag, source), a body with template variables, an email subject, an optional spend cap and a per-send cost estimate in minor units. An email needs a subject. Nothing is sent. Audited. Needs the outreach:write scope or an admin session.","operationId":"postAdminOutreachCampaigns","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"channel":{"type":"string","enum":["call","email","text","postcard"]},"audience":{"type":"object","properties":{"status":{"type":"string","minLength":1,"maxLength":20},"tag":{"type":"string","minLength":1,"maxLength":40},"source":{"type":"string","minLength":1,"maxLength":60}},"additionalProperties":false},"subject":{"type":"string","minLength":1,"maxLength":200,"pattern":"^[^\\r\\n]+$"},"body":{"type":"string","minLength":1,"maxLength":5000},"spendCapCents":{"type":"integer","minimum":1,"maximum":100000000},"unitCostCents":{"default":0,"type":"integer","minimum":0,"maximum":100000}},"required":["name","channel","audience","body"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/outreach/contacts":{"get":{"summary":"The CRM contacts a campaign could reach on one channel, 50 a page, oldest first by created time. Filter by status, tag and source. Each row says whether it is reachable and, if not, why: no_lead, no_phone, bad_phone, opted_out, no_email, suppressed, no_address or duplicate. Needs the outreach:read scope or an admin session.","operationId":"getAdminOutreachContacts","parameters":[{"name":"channel","in":"query","required":true,"schema":{"type":"string","enum":["call","email","text","postcard"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":20}},{"name":"tag","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":40}},{"name":"source","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":60}},{"name":"page","in":"query","required":false,"schema":{"default":1,"type":"integer","minimum":1,"maximum":10000}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["contact","lead"]},"contactId":{"type":["string","null"]},"leadId":{"type":["string","null"]},"name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"status":{"type":"string"},"source":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"listingId":{"type":["string","null"]},"reachable":{"type":"boolean"},"reason":{"anyOf":[{"type":"string","enum":["no_lead","no_phone","bad_phone","opted_out","no_email","suppressed","no_address","duplicate"]},{"type":"null"}]}},"required":["id","kind","contactId","leadId","name","email","phone","status","source","tags","listingId","reachable","reason"],"additionalProperties":false}},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","total","page","pageSize"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/outreach/targets/{id}":{"post":{"summary":"Pause, resume, mark replied or complained, draft the one follow-up, or set the time zone of an outreach target. A complaint suppresses the address and pauses the sender. Session only.","operationId":"postAdminOutreachTargetsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","enum":["pause","resume","replied","followup","complaint"]}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"timezone"},"timezone":{"type":"string","minLength":3,"maxLength":60}},"required":["action","timezone"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"messageId":{"type":"string"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/products":{"get":{"summary":"Every product, hidden ones too, by category, featured, rank. Deleted ones only with include_deleted=1. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"getAdminProducts","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","hidden"]}},{"name":"include_deleted","in":"query","required":false,"schema":{"type":"string","enum":["0","1","true","false"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"category":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200},"brand":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":2000},"retailer":{"type":"string","enum":["amazon","etsy","other"]},"url":{"type":"string","maxLength":2000},"price_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"currency":{"type":"string"},"price_as_of":{"type":"string"},"image_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"image_alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"image_source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"images":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"alt":{"type":"string","minLength":1,"maxLength":300},"source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url","alt"],"additionalProperties":false}},"source":{"type":"string","minLength":1,"maxLength":300},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"featured":{"default":false,"type":"boolean"},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"created_by":{"type":["string","null"]},"updated_by":{"type":["string","null"]},"deleted_at":{"type":["string","null"]}},"required":["id","slug","category","title","brand","description","retailer","url","price_cents","currency","price_as_of","images","source","tags","featured","rank","status","created_at","updated_at","created_by","updated_by","deleted_at"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create or replace one product by slug: an idempotent upsert that also restores a deleted one. The URL must be the plain retailer page. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"postAdminProducts","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"category":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200},"brand":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":2000},"retailer":{"type":"string","enum":["amazon","etsy","other"]},"url":{"type":"string","maxLength":2000},"price_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"currency":{"type":"string"},"price_as_of":{"type":"string"},"image_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"image_alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"image_source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"images":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"alt":{"type":"string","minLength":1,"maxLength":300},"source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url","alt"],"additionalProperties":false}},"source":{"type":"string","minLength":1,"maxLength":300},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"featured":{"default":false,"type":"boolean"},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]}},"required":["slug","category","title","brand","description","retailer","url","price_cents","currency","price_as_of","source"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/products/bulk":{"post":{"summary":"Upsert up to 200 products by slug from a JSON array, { rows }, or a text/csv body (tags as a|b). Every row is checked first; one bad row writes nothing. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"postAdminProductsBulk","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"category":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200},"brand":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":2000},"retailer":{"type":"string","enum":["amazon","etsy","other"]},"url":{"type":"string","maxLength":2000},"price_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"currency":{"type":"string"},"price_as_of":{"type":"string"},"image_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"image_alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"image_source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"images":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"alt":{"type":"string","minLength":1,"maxLength":300},"source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url","alt"],"additionalProperties":false}},"source":{"type":"string","minLength":1,"maxLength":300},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"featured":{"default":false,"type":"boolean"},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]}},"required":["slug","category","title","brand","description","retailer","url","price_cents","currency","price_as_of","source"],"additionalProperties":false}},{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"category":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200},"brand":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":2000},"retailer":{"type":"string","enum":["amazon","etsy","other"]},"url":{"type":"string","maxLength":2000},"price_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"currency":{"type":"string"},"price_as_of":{"type":"string"},"image_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"image_alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"image_source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"images":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"alt":{"type":"string","minLength":1,"maxLength":300},"source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url","alt"],"additionalProperties":false}},"source":{"type":"string","minLength":1,"maxLength":300},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"featured":{"default":false,"type":"boolean"},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]}},"required":["slug","category","title","brand","description","retailer","url","price_cents","currency","price_as_of","source"],"additionalProperties":false}}},"required":["rows"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"counts":{"type":"object","properties":{"rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updated":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unchanged":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["rows","created","updated","unchanged"],"additionalProperties":false},"rows":{"type":"array","items":{"type":"object","properties":{"line":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"slug":{"type":"string"},"action":{"type":"string","enum":["created","updated","unchanged"]}},"required":["line","slug","action"],"additionalProperties":false}}},"required":["ok","counts","rows"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/products/{slug}":{"get":{"summary":"One product, deleted or not. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"getAdminProductsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"category":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200},"brand":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":2000},"retailer":{"type":"string","enum":["amazon","etsy","other"]},"url":{"type":"string","maxLength":2000},"price_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"currency":{"type":"string"},"price_as_of":{"type":"string"},"image_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"image_alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"image_source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"images":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"alt":{"type":"string","minLength":1,"maxLength":300},"source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url","alt"],"additionalProperties":false}},"source":{"type":"string","minLength":1,"maxLength":300},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"featured":{"default":false,"type":"boolean"},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"created_by":{"type":["string","null"]},"updated_by":{"type":["string","null"]},"deleted_at":{"type":["string","null"]}},"required":["id","slug","category","title","brand","description","retailer","url","price_cents","currency","price_as_of","images","source","tags","featured","rank","status","created_at","updated_at","created_by","updated_by","deleted_at"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Change some fields of one product. The result must pass the full product checks. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"patchAdminProductsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200},"brand":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":2000},"retailer":{"type":"string","enum":["amazon","etsy","other"]},"url":{"type":"string","maxLength":2000},"price_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"currency":{"type":"string"},"price_as_of":{"type":"string"},"image_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"image_alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"image_source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"images":{"maxItems":12,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"alt":{"type":"string","minLength":1,"maxLength":300},"source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url","alt"],"additionalProperties":false}},"source":{"type":"string","minLength":1,"maxLength":300},"tags":{"maxItems":20,"type":"array","items":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"featured":{"type":"boolean"},"rank":{"type":"integer","minimum":0,"maximum":9999},"status":{"type":"string","enum":["active","hidden"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Soft-delete one product: it leaves the site and keeps its row. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"deleteAdminProductsBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/product-categories":{"get":{"summary":"Every product category, hidden ones too. Deleted ones only with include_deleted=1. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"getAdminProductCategories","parameters":[{"name":"include_deleted","in":"query","required":false,"schema":{"type":"string","enum":["0","1","true","false"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":500},"group":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"created_by":{"type":["string","null"]},"updated_by":{"type":["string","null"]},"deleted_at":{"type":["string","null"]}},"required":["slug","name","description","rank","status","created_at","updated_at","created_by","updated_by","deleted_at"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create or replace one product category by slug: an idempotent upsert that also restores a deleted one. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"postAdminProductCategories","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":500},"group":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]}},"required":["slug","name","description"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/product-categories/bulk":{"post":{"summary":"Upsert up to 200 product categories by slug from a JSON array, { rows }, or a text/csv body. One bad row writes nothing. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"postAdminProductCategoriesBulk","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":500},"group":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]}},"required":["slug","name","description"],"additionalProperties":false}},{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":500},"group":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]}},"required":["slug","name","description"],"additionalProperties":false}}},"required":["rows"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"counts":{"type":"object","properties":{"rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updated":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unchanged":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["rows","created","updated","unchanged"],"additionalProperties":false},"rows":{"type":"array","items":{"type":"object","properties":{"line":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"slug":{"type":"string"},"action":{"type":"string","enum":["created","updated","unchanged"]}},"required":["line","slug","action"],"additionalProperties":false}}},"required":["ok","counts","rows"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/product-categories/{slug}":{"get":{"summary":"One product category, deleted or not. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"getAdminProductCategoriesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":500},"group":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"created_by":{"type":["string","null"]},"updated_by":{"type":["string","null"]},"deleted_at":{"type":["string","null"]}},"required":["slug","name","description","rank","status","created_at","updated_at","created_by","updated_by","deleted_at"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Change some fields of one product category. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"patchAdminProductCategoriesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":500},"group":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"status":{"default":"active","type":"string","enum":["active","hidden"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Soft-delete one product category. Its products leave the site with it and keep their rows. Needs the products:write scope (or PRODUCTS_ADMIN_TOKEN) or an admin session.","operationId":"deleteAdminProductCategoriesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/templates":{"get":{"summary":"Every certificate template, the default first. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyTemplates","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"design":{"type":"object","properties":{"palette":{"type":"object","properties":{"background":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"text":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"accent":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["background","text","accent"],"additionalProperties":false},"fontPair":{"type":"string","enum":["serif","sans","serif-sans"]},"layout":{"type":"string","enum":["classic","modern","minimal"]},"signatoryName":{"type":"string","minLength":1,"maxLength":120},"signatoryTitle":{"type":"string","maxLength":120},"sealText":{"type":"string","maxLength":40},"footer":{"type":"string","maxLength":300},"logoKey":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]}},"required":["palette","fontPair","layout","signatoryName","signatoryTitle","sealText","footer","logoKey"],"additionalProperties":false},"is_default":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","slug","name","design","is_default","created_at","updated_at"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/templates/{slug}":{"get":{"summary":"One certificate template. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyTemplatesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"design":{"type":"object","properties":{"palette":{"type":"object","properties":{"background":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"text":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"accent":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["background","text","accent"],"additionalProperties":false},"fontPair":{"type":"string","enum":["serif","sans","serif-sans"]},"layout":{"type":"string","enum":["classic","modern","minimal"]},"signatoryName":{"type":"string","minLength":1,"maxLength":120},"signatoryTitle":{"type":"string","maxLength":120},"sealText":{"type":"string","maxLength":40},"footer":{"type":"string","maxLength":300},"logoKey":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]}},"required":["palette","fontPair","layout","signatoryName","signatoryTitle","sealText","footer","logoKey"],"additionalProperties":false},"is_default":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","slug","name","design","is_default","created_at","updated_at"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Create or replace one certificate template by slug: an idempotent upsert that leaves the default flag alone. Needs the academy:write scope or an admin session.","operationId":"putAdminAcademyTemplatesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"design":{"type":"object","properties":{"palette":{"type":"object","properties":{"background":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"text":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"accent":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["background","text","accent"],"additionalProperties":false},"fontPair":{"type":"string","enum":["serif","sans","serif-sans"]},"layout":{"type":"string","enum":["classic","modern","minimal"]},"signatoryName":{"type":"string","minLength":1,"maxLength":120},"signatoryTitle":{"type":"string","maxLength":120},"sealText":{"type":"string","maxLength":40},"footer":{"type":"string","maxLength":300},"logoKey":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]}},"required":["palette","fontPair","layout","signatoryName","signatoryTitle","sealText","footer"],"additionalProperties":false}},"required":["name","design"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete one certificate template. Courses that used it lose the link; issued certificates keep their own copy of the design. Deleting the default leaves the site without one, and certificates then use the built-in design. Needs the academy:write scope or an admin session.","operationId":"deleteAdminAcademyTemplatesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/templates/{slug}/default":{"post":{"summary":"Make one certificate template the default. The old default is cleared in the same write, so there is exactly one; a template that already is the default changes nothing. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyTemplatesBySlugDefault","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/lessons":{"get":{"summary":"The lessons of a course by position, then slug. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCoursesBySlugLessons","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"course_id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"body":{"type":"string"},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"duration_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","course_id","slug","title","body","position","duration_minutes","published","created_at","updated_at"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/lessons/{lesson}":{"get":{"summary":"One lesson of a course. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCoursesBySlugLessonsByLesson","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}},{"name":"lesson","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"course_id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"body":{"type":"string"},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"duration_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","course_id","slug","title","body","position","duration_minutes","published","created_at","updated_at"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Create or replace one lesson by slug: an idempotent upsert. The slug is unique in the course and may not be a reserved page name such as exam. Positions are not unique: equal ones sort by slug. Needs the academy:write scope or an admin session.","operationId":"putAdminAcademyCoursesBySlugLessonsByLesson","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}},{"name":"lesson","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"body":{"type":"string","minLength":1,"maxLength":40000},"position":{"type":"integer","minimum":0,"maximum":10000},"durationMinutes":{"type":"integer","minimum":0,"maximum":1000},"published":{"default":false,"type":"boolean"}},"required":["title","body","position","durationMinutes"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Hard-delete one lesson. Its questions stay and lose their lesson. Needs the academy:write scope or an admin session.","operationId":"deleteAdminAcademyCoursesBySlugLessonsByLesson","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}},{"name":"lesson","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/images":{"get":{"summary":"The images of a course, oldest first, with the site path of each. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCoursesBySlugImages","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"contentType":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"alt":{"type":"string"},"caption":{"type":["string","null"]}},"required":["id","url","contentType","width","height","sizeBytes","alt","caption"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Add one image to a course. Send a multipart body with a file field, alt and optional caption, or JSON with contentBase64, alt and optional caption. PNG, JPEG or WebP only, within the photo size and pixel limits; alt text is required. The answer carries the item url: put it in a lesson body as ![alt](url). Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCoursesBySlugImages","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"alt":{"type":"string","minLength":1,"maxLength":300},"caption":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"contentBase64":{"type":"string","minLength":1,"maxLength":8000000,"description":"The PNG, JPEG or WebP file, base64 encoded."}},"required":["alt","contentBase64"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"contentType":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"alt":{"type":"string"},"caption":{"type":["string","null"]}},"required":["id","url","contentType","width","height","sizeBytes","alt","caption"],"additionalProperties":false}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/images/{id}":{"delete":{"summary":"Hard-delete one image and its stored file. A lesson that names it shows no image there. Needs the academy:write scope or an admin session.","operationId":"deleteAdminAcademyCoursesBySlugImagesById","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/questions":{"get":{"summary":"The questions of a course, oldest first, with the correct flag of each option. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCoursesBySlugQuestions","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"course_id":{"type":"string"},"lesson_id":{"type":["string","null"]},"kind":{"type":"string","enum":["single","multi","scenario","ordering","short"]},"prompt":{"type":"string"},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"correct":{"type":"boolean"}},"required":["id","text","correct"],"additionalProperties":false}},"explanation":{"type":"string"},"weight":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","course_id","lesson_id","kind","prompt","options","explanation","weight","published","created_at","updated_at"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create one exam question; the answer carries its qst_ id. single and scenario need 2 to 8 options, exactly one correct; multi needs 2 to 8, at least one correct and one incorrect; ordering needs 3 to 8 steps in the right order, each correct; short needs 1 to 10 accepted answers of up to 80 characters, each correct. Option ids are unique. lessonId must be a lesson of the same course. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCoursesBySlugQuestions","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["single","multi","scenario","ordering","short"]},"prompt":{"type":"string","minLength":1,"maxLength":2000},"options":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":40},"text":{"type":"string","minLength":1,"maxLength":500},"correct":{"type":"boolean"}},"required":["id","text","correct"],"additionalProperties":false},"description":"single and scenario: 2 to 8 options, one correct. multi: 2 to 8, some correct and some not. ordering: 3 to 8 steps in the right order, each correct. short: 1 to 10 accepted answers of up to 80 characters, each correct; case and extra spaces do not count."},"explanation":{"default":"","type":"string","maxLength":2000},"weight":{"default":1,"type":"integer","minimum":1,"maximum":100},"published":{"default":false,"type":"boolean"},"lessonId":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":60},{"type":"null"}]}},"required":["kind","prompt","options"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/questions/{id}":{"get":{"summary":"One question by qst_ id, with the correct flag of each option. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCoursesBySlugQuestionsById","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"course_id":{"type":"string"},"lesson_id":{"type":["string","null"]},"kind":{"type":"string","enum":["single","multi","scenario","ordering","short"]},"prompt":{"type":"string"},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"correct":{"type":"boolean"}},"required":["id","text","correct"],"additionalProperties":false}},"explanation":{"type":"string"},"weight":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","course_id","lesson_id","kind","prompt","options","explanation","weight","published","created_at","updated_at"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Replace one question by qst_ id; the same checks as create. Needs the academy:write scope or an admin session.","operationId":"putAdminAcademyCoursesBySlugQuestionsById","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["single","multi","scenario","ordering","short"]},"prompt":{"type":"string","minLength":1,"maxLength":2000},"options":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":40},"text":{"type":"string","minLength":1,"maxLength":500},"correct":{"type":"boolean"}},"required":["id","text","correct"],"additionalProperties":false},"description":"single and scenario: 2 to 8 options, one correct. multi: 2 to 8, some correct and some not. ordering: 3 to 8 steps in the right order, each correct. short: 1 to 10 accepted answers of up to 80 characters, each correct; case and extra spaces do not count."},"explanation":{"default":"","type":"string","maxLength":2000},"weight":{"default":1,"type":"integer","minimum":1,"maximum":100},"published":{"default":false,"type":"boolean"},"lessonId":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":60},{"type":"null"}]}},"required":["kind","prompt","options"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Hard-delete one question by qst_ id. Needs the academy:write scope or an admin session.","operationId":"deleteAdminAcademyCoursesBySlugQuestionsById","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses":{"get":{"summary":"Courses by sort order, with lesson and question counts, all and published. Deleted ones only with includeDeleted=1. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCourses","parameters":[{"name":"includeDeleted","in":"query","required":false,"schema":{"type":"string","enum":["0","1"]}},{"name":"level","in":"query","required":false,"schema":{"type":"string","enum":["foundations","practitioner","specialist"]}},{"name":"published","in":"query","required":false,"schema":{"type":"string","enum":["0","1"]}},{"name":"limit","in":"query","required":false,"description":"Courses per page: 1 to 50, default 50.","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"body":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"duration_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pass_mark_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"max_attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"validity_months":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Months a certificate stays valid. Null: it never expires."},"exam_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Exam time limit in minutes. Null: 90 seconds a question, at least 5 minutes."},"template_id":{"type":["string","null"]},"objectives":{"type":"array","items":{"type":"string"}},"faq":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"}},"required":["question","answer"],"additionalProperties":false}},"seo_title":{"type":["string","null"]},"seo_description":{"type":["string","null"]},"published":{"type":"boolean"},"sort_order":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lesson_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published_lesson_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"question_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published_question_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_by":{"type":["string","null"]},"updated_by":{"type":["string","null"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"deleted_at":{"type":["string","null"]}},"required":["id","slug","title","summary","body","level","duration_minutes","pass_mark_percent","max_attempts","validity_months","exam_minutes","template_id","objectives","faq","seo_title","seo_description","published","sort_order","lesson_count","published_lesson_count","question_count","published_question_count","created_by","updated_by","created_at","updated_at","deleted_at"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}":{"get":{"summary":"One live course with its lesson and question counts. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCoursesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"body":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"duration_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pass_mark_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"max_attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"validity_months":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Months a certificate stays valid. Null: it never expires."},"exam_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Exam time limit in minutes. Null: 90 seconds a question, at least 5 minutes."},"template_id":{"type":["string","null"]},"objectives":{"type":"array","items":{"type":"string"}},"faq":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"}},"required":["question","answer"],"additionalProperties":false}},"seo_title":{"type":["string","null"]},"seo_description":{"type":["string","null"]},"published":{"type":"boolean"},"sort_order":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lesson_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published_lesson_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"question_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"published_question_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_by":{"type":["string","null"]},"updated_by":{"type":["string","null"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"deleted_at":{"type":["string","null"]}},"required":["id","slug","title","summary","body","level","duration_minutes","pass_mark_percent","max_attempts","validity_months","exam_minutes","template_id","objectives","faq","seo_title","seo_description","published","sort_order","lesson_count","published_lesson_count","question_count","published_question_count","created_by","updated_by","created_at","updated_at","deleted_at"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Create or replace one course by slug: an idempotent upsert. published sets whether it is live; going live needs a published question with a correct option, or the answer is a 409 course_not_ready. A soft-deleted slug is a 409 slug_deleted unless restore is true. Needs the academy:write scope or an admin session.","operationId":"putAdminAcademyCoursesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":160},"summary":{"type":"string","minLength":1,"maxLength":500},"body":{"default":"","type":"string","maxLength":60000},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"durationMinutes":{"type":"integer","minimum":0,"maximum":100000},"passMarkPercent":{"default":null,"description":"Null uses the site default.","anyOf":[{"type":"integer","minimum":1,"maximum":100},{"type":"null"}]},"maxAttempts":{"default":null,"description":"Null uses the site default.","anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":1000},{"type":"null"}]},"validityMonths":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":1200},{"type":"null"}],"description":"Months a certificate stays valid. Null: it never expires."},"examMinutes":{"default":null,"description":"Exam time limit in minutes. Null: 90 seconds a question, at least 5 minutes.","anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":600},{"type":"null"}]},"templateId":{"default":null,"description":"The id of an existing certificate template, or null.","anyOf":[{"type":"string","minLength":1,"maxLength":60},{"type":"null"}]},"objectives":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":300}},"faq":{"default":[],"maxItems":30,"type":"array","items":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":300},"answer":{"type":"string","minLength":1,"maxLength":2000}},"required":["question","answer"],"additionalProperties":false}},"seoTitle":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":70},{"type":"null"}]},"seoDescription":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":160},{"type":"null"}]},"sortOrder":{"default":0,"type":"integer","minimum":0,"maximum":100000},"published":{"type":"boolean"},"restore":{"default":false,"description":"Set true to bring back a soft-deleted course at this slug.","type":"boolean"}},"required":["title","summary","level","durationMinutes","validityMonths","published"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Soft-delete one course: the row and its certificates stay. Needs the academy:write scope or an admin session.","operationId":"deleteAdminAcademyCoursesBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/publish":{"post":{"summary":"Publish one course. Repeating it changes nothing. A course with no published exam question, or with a published question that has no correct option (or, for a single or scenario question, more than one), an ordering question without 3 to 8 steps, or a short question with no accepted answer, is a 409 course_not_ready whose details list what is missing. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCoursesBySlugPublish","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/courses/{slug}/unpublish":{"post":{"summary":"Unpublish one course. Repeating it changes nothing. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCoursesBySlugUnpublish","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":80}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","slug","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/students":{"get":{"summary":"Users with an enrollment, a certificate or a membership, the most recently active first, with counts and whether they have access now. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyStudents","parameters":[{"name":"q","in":"query","required":false,"description":"Email or name prefix.","schema":{"type":"string","minLength":1,"maxLength":100}},{"name":"limit","in":"query","required":false,"description":"Rows per page: 1 to 50, default 25.","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"enrollment_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"certificate_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"membership_active":{"type":"boolean"},"membership_source":{"anyOf":[{"type":"string","enum":["stripe","comp"]},{"type":"null"}]},"last_activity_at":{"type":["string","null"]}},"required":["id","email","name","enrollment_count","certificate_count","membership_active","membership_source","last_activity_at"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/students/{userId}":{"get":{"summary":"One student with enrollments, attempt scores and pass flags (never answers), certificates and memberships. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyStudentsByUserId","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"membership_active":{"type":"boolean"},"enrollments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"course_slug":{"type":"string"},"course_title":{"type":"string"},"status":{"type":"string"},"completed_lesson_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"started_at":{"type":"string"},"completed_at":{"type":["string","null"]}},"required":["id","course_slug","course_title","status","completed_lesson_count","started_at","completed_at"],"additionalProperties":false}},"attempts":{"type":"array","items":{"type":"object","properties":{"course_slug":{"type":"string"},"attempt_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"score_percent":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"passed":{"type":["boolean","null"]},"submitted_at":{"type":["string","null"]}},"required":["course_slug","attempt_number","score_percent","passed","submitted_at"],"additionalProperties":false}},"certificates":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"course_title":{"type":"string"},"status":{"type":"string"},"issued_at":{"type":"string"}},"required":["code","course_title","status","issued_at"],"additionalProperties":false}},"memberships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string","enum":["stripe","comp"]},"status":{"type":"string"},"active":{"type":"boolean"},"current_period_end":{"type":["string","null"]},"cancel_at_period_end":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","source","status","active","current_period_end","cancel_at_period_end","created_at"],"additionalProperties":false}}},"required":["id","email","name","membership_active","enrollments","attempts","certificates","memberships"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/memberships":{"get":{"summary":"Membership rows, newest first, with the owner's email. active is decided per row at the request time. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyMemberships","parameters":[{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["stripe","comp"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":40}},{"name":"active","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","required":false,"description":"Rows per page: 1 to 50, default 25.","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string","enum":["stripe","comp"]},"status":{"type":"string"},"active":{"type":"boolean"},"current_period_end":{"type":["string","null"]},"cancel_at_period_end":{"type":"boolean"},"created_at":{"type":"string"},"user_id":{"type":"string"},"email":{"type":["string","null"]}},"required":["id","source","status","active","current_period_end","cancel_at_period_end","created_at","user_id","email"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Grant a comp membership to a user. A user with an active membership is a 409 already_member; an unknown user is a 404; endsAt must be in the future. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyMemberships","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","minLength":1,"maxLength":60},"endsAt":{"default":null,"description":"When the comp ends (ISO 8601, in the future). Null: no end.","anyOf":[{"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))$"},{"type":"null"}]}},"required":["userId"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/memberships/{id}":{"delete":{"summary":"Revoke a comp membership: the row stays as history and access ends now. A Stripe membership is a 409 stripe_membership (the billing admin owns it); one already revoked is a 409. Needs the academy:write scope or an admin session.","operationId":"deleteAdminAcademyMembershipsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/certificates":{"get":{"summary":"Certificates, newest first, with the revoke reason. Filter by status (valid, expired or revoked, computed from the dates), course slug and code (full or partial, any spelling; a partial matches the start). Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCertificates","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["valid","expired","revoked"]}},{"name":"course","in":"query","required":false,"schema":{"type":"string","maxLength":80}},{"name":"code","in":"query","required":false,"description":"A full or partial code in any spelling; a partial matches the start of the code.","schema":{"type":"string","pattern":"^(?=.*[0-9A-Za-z])[0-9A-Za-z\\s-]{1,16}$"}},{"name":"limit","in":"query","required":false,"description":"Certificates per page: 1 to 50, default 25.","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"The code as shown, XXXX-XXXX-XX."},"status":{"type":"string","enum":["valid","expired","revoked"]},"course_slug":{"type":"string"},"course_title":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"score_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"show_name":{"type":"boolean"},"issued_at":{"type":"string"},"expires_at":{"type":["string","null"]},"revoked_at":{"type":["string","null"]},"revoked_reason":{"description":"Admins only: the public lookup never shows it.","type":["string","null"]},"user_id":{"type":["string","null"]},"attempt_id":{"description":"Null for a certificate issued by hand.","type":["string","null"]},"recipient_name":{"type":"string"}},"required":["id","code","status","course_slug","course_title","level","score_percent","show_name","issued_at","expires_at","revoked_at","revoked_reason","user_id","attempt_id","recipient_name"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Issue one certificate by hand, with no exam attempt. The admin is the audit actor, and the learner gets the certificate email as for an exam pass. 422 for a bad score or name, an unknown user, or an unknown or unpublished course. 409 already_issued when the learner already holds an unrevoked certificate for the course. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCertificates","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","minLength":1,"maxLength":60},"courseSlug":{"type":"string","maxLength":80},"recipientName":{"type":"string","description":"The name printed on the certificate, 1 to 120 characters."},"scorePercent":{"type":"number","description":"A whole number from 0 to 100."},"showName":{"default":false,"type":"boolean"}},"required":["userId","courseSlug","recipientName","scorePercent"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"The code as shown, XXXX-XXXX-XX."},"status":{"type":"string","enum":["valid","expired","revoked"]},"course_slug":{"type":"string"},"course_title":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"score_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"show_name":{"type":"boolean"},"issued_at":{"type":"string"},"expires_at":{"type":["string","null"]},"revoked_at":{"type":["string","null"]},"revoked_reason":{"description":"Admins only: the public lookup never shows it.","type":["string","null"]},"user_id":{"type":["string","null"]},"attempt_id":{"description":"Null for a certificate issued by hand.","type":["string","null"]},"recipient_name":{"type":"string"}},"required":["id","code","status","course_slug","course_title","level","score_percent","show_name","issued_at","expires_at","revoked_at","revoked_reason","user_id","attempt_id","recipient_name"],"additionalProperties":false}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/certificates/{code}":{"get":{"summary":"One certificate by code, in any spelling: upper or lower case, with or without dashes. 404 certificate_not_found for an invalid or unknown code. Needs the academy:read scope or an admin session.","operationId":"getAdminAcademyCertificatesByCode","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":40}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"The code as shown, XXXX-XXXX-XX."},"status":{"type":"string","enum":["valid","expired","revoked"]},"course_slug":{"type":"string"},"course_title":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"score_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"show_name":{"type":"boolean"},"issued_at":{"type":"string"},"expires_at":{"type":["string","null"]},"revoked_at":{"type":["string","null"]},"revoked_reason":{"description":"Admins only: the public lookup never shows it.","type":["string","null"]},"user_id":{"type":["string","null"]},"attempt_id":{"description":"Null for a certificate issued by hand.","type":["string","null"]},"recipient_name":{"type":"string"}},"required":["id","code","status","course_slug","course_title","level","score_percent","show_name","issued_at","expires_at","revoked_at","revoked_reason","user_id","attempt_id","recipient_name"],"additionalProperties":false}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/certificates/{code}/revoke":{"post":{"summary":"Revoke one certificate with a reason of 3 to 300 characters. The public lookup then answers revoked at once and never shows the reason. 409 already_revoked. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCertificatesByCodeRevoke","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":40}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":3,"maxLength":300}},"required":["reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"The code as shown, XXXX-XXXX-XX."},"status":{"type":"string","enum":["valid","expired","revoked"]},"course_slug":{"type":"string"},"course_title":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"score_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"show_name":{"type":"boolean"},"issued_at":{"type":"string"},"expires_at":{"type":["string","null"]},"revoked_at":{"type":["string","null"]},"revoked_reason":{"description":"Admins only: the public lookup never shows it.","type":["string","null"]},"user_id":{"type":["string","null"]},"attempt_id":{"description":"Null for a certificate issued by hand.","type":["string","null"]},"recipient_name":{"type":"string"}},"required":["id","code","status","course_slug","course_title","level","score_percent","show_name","issued_at","expires_at","revoked_at","revoked_reason","user_id","attempt_id","recipient_name"],"additionalProperties":false}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/certificates/{code}/reinstate":{"post":{"summary":"Clear the revoke of one certificate; it reads valid or expired again by its dates. 409 not_revoked. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCertificatesByCodeReinstate","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":40}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"The code as shown, XXXX-XXXX-XX."},"status":{"type":"string","enum":["valid","expired","revoked"]},"course_slug":{"type":"string"},"course_title":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"score_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"show_name":{"type":"boolean"},"issued_at":{"type":"string"},"expires_at":{"type":["string","null"]},"revoked_at":{"type":["string","null"]},"revoked_reason":{"description":"Admins only: the public lookup never shows it.","type":["string","null"]},"user_id":{"type":["string","null"]},"attempt_id":{"description":"Null for a certificate issued by hand.","type":["string","null"]},"recipient_name":{"type":"string"}},"required":["id","code","status","course_slug","course_title","level","score_percent","show_name","issued_at","expires_at","revoked_at","revoked_reason","user_id","attempt_id","recipient_name"],"additionalProperties":false}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/academy/certificates/{code}/extend":{"post":{"summary":"Set a later expiry (an ISO date in the future, after the current one) or neverExpires: true. 409 revoked_cannot_extend for a revoked certificate; 409 not_an_extension for a date that is not later than the current expiry, and for any date on a certificate that never expires. Needs the academy:write scope or an admin session.","operationId":"postAdminAcademyCertificatesByCodeExtend","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":40}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expiresAt":{"type":"string"},"neverExpires":{"type":"boolean","const":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"item":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"The code as shown, XXXX-XXXX-XX."},"status":{"type":"string","enum":["valid","expired","revoked"]},"course_slug":{"type":"string"},"course_title":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"score_percent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"show_name":{"type":"boolean"},"issued_at":{"type":"string"},"expires_at":{"type":["string","null"]},"revoked_at":{"type":["string","null"]},"revoked_reason":{"description":"Admins only: the public lookup never shows it.","type":["string","null"]},"user_id":{"type":["string","null"]},"attempt_id":{"description":"Null for a certificate issued by hand.","type":["string","null"]},"recipient_name":{"type":"string"}},"required":["id","code","status","course_slug","course_title","level","score_percent","show_name","issued_at","expires_at","revoked_at","revoked_reason","user_id","attempt_id","recipient_name"],"additionalProperties":false}},"required":["ok","item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/places":{"put":{"summary":"Create or replace one place by its path. Needs the places:write scope or an admin session.","operationId":"putAdminPlaces","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","maxLength":120,"pattern":"^[a-z]{2}(\\/[a-z0-9]+(-[a-z0-9]+)*){0,2}$"},"name":{"type":"string","minLength":1,"maxLength":120},"names":{"type":"object","propertyNames":{"type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]+)*$"},"additionalProperties":{"type":"string","minLength":1,"maxLength":120}},"regionCode":{"anyOf":[{"type":"string","minLength":1,"maxLength":10},{"type":"null"}]},"population":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"lat":{"anyOf":[{"type":"number","minimum":-90,"maximum":90},{"type":"null"}]},"lng":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"null"}]}},"required":["path","name"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"kind":{"type":"string","enum":["country","region","city"]},"created":{"type":"boolean"},"changed":{"type":"array","items":{"type":"string"}}},"required":["ok","id","kind","created","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Delete one place that has no child places and no listings. Needs the places:write scope or an admin session.","operationId":"deleteAdminPlaces","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","maxLength":120,"pattern":"^[a-z]{2}(\\/[a-z0-9]+(-[a-z0-9]+)*){0,2}$"}},"required":["path"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/users":{"get":{"summary":"The accounts, newest first, with role, admin flag, live session count and owned listing count. Pass nextCursor as cursor until it is null. Needs the users:manage scope or an admin session.","operationId":"getAdminUsers","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":254}},{"name":"limit","in":"query","required":false,"description":"Accounts per page: 1 to 500, default 100.","schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","required":false,"description":"Pass the previous nextCursor with the same q filter. Accounts sort by creation time, then ID, both descending.","schema":{"type":"string","pattern":"^(\\d{1,15})\\.([A-Za-z0-9_-]{1,128})$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["member","admin"]},"emailVerified":{"type":"boolean"},"isAdmin":{"type":"boolean"},"createdAt":{"type":"string"},"deactivatedAt":{"type":["string","null"]},"sessions":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"listings":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","email","name","role","emailVerified","isAdmin","createdAt","deactivatedAt","sessions","listings"],"additionalProperties":false}},"nextCursor":{"description":"Cursor for the next page, or null when no more accounts remain.","type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create one account without sending mail. The role defaults to member. A repeat of an address, in any letter case, is a 409. Needs the users:manage scope or an admin session.","operationId":"postAdminUsers","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":254},"name":{"type":"string","minLength":1,"maxLength":255},"role":{"default":"member","type":"string","enum":["member","admin"]}},"required":["email"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["member","admin"]}},"required":["ok","id","email","role"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/users/invite":{"post":{"summary":"Invite an address: create the account with the role, or reactivate a deactivated one, and mail the sign-in link (the admin host for an admin). A pending invite is sent again with the new role. An account that has signed in is a 409. Needs the users:manage scope or an admin session.","operationId":"postAdminUsersInvite","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":254},"role":{"default":"member","type":"string","enum":["member","admin"]}},"required":["email"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["member","admin"]},"created":{"type":"boolean"},"reactivated":{"type":"boolean"}},"required":["ok","id","email","role","created","reactivated"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/users/{id}/sessions":{"get":{"summary":"The live sessions of one account, newest first: id, dates, address and user agent. Never the token. Needs the users:manage scope or an admin session.","operationId":"getAdminUsersByIdSessions","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"ipAddress":{"type":["string","null"]},"userAgent":{"type":["string","null"]}},"required":["id","createdAt","expiresAt","ipAddress","userAgent"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Revoke one session of the account (sessionId) or all of them. A session of another account is a 404. Revoking nothing writes no audit row. Needs the users:manage scope or an admin session.","operationId":"deleteAdminUsersByIdSessions","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"revoked":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["ok","revoked"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/users/{id}":{"delete":{"summary":"Offboard one account. It is deactivated: its sessions are revoked, its role goes back to member and it cannot sign in. The row stays, with its claimed listings. A repeat changes nothing. An admin session cannot name its own account. Needs the users:manage scope or an admin session.","operationId":"deleteAdminUsersById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"changed":{"type":"boolean"},"sessionsRevoked":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"listings":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["ok","id","changed","sessionsRevoked","listings"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"patch":{"summary":"Set one account's role. `isAdmin` in the answer is the effective flag: an address on the allowlist stays an admin whatever its role. Needs the users:manage scope or an admin session.","operationId":"patchAdminUsersById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["member","admin"]}},"required":["role"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"role":{"type":"string","enum":["member","admin"]},"isAdmin":{"type":"boolean"},"changed":{"type":"boolean"}},"required":["ok","id","role","isAdmin","changed"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/webhooks":{"get":{"summary":"The outbound Webhooks.","operationId":"getAdminWebhooks","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Register one Webhook.","operationId":"postAdminWebhooks","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"secret":{"type":"string","minLength":16},"events":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["url","secret","events"]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"item":{}},"required":["item"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/webhooks/{id}":{"delete":{"summary":"Remove one Webhook.","operationId":"deleteAdminWebhooksById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/video/jobs":{"get":{"summary":"The social video jobs, newest first. Needs the video:write scope or an admin session.","operationId":"getAdminVideoJobs","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"post":{"summary":"Create a social video draft that waits for the owner. Needs the video:write scope or an admin session.","operationId":"postAdminVideoJobs","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120},"caption":{"type":"string","maxLength":1000},"accounts":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}},"assetKey":{"type":"string","maxLength":300},"costCents":{"type":"integer","minimum":0,"maximum":100000}},"required":["title"]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"}},"required":["ok","id"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/video/jobs/{id}":{"patch":{"summary":"Approve, schedule, cancel or kill a video job. Needs the video:write scope or an admin session.","operationId":"patchAdminVideoJobsById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","const":"approve"},"publishAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"schedule"},"publishAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["action","publishAt"]},{"type":"object","properties":{"action":{"type":"string","const":"cancel"}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","const":"kill"}},"required":["action"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/video/queue":{"get":{"summary":"The publish queue: due jobs, the kill switch and spend against the caps. Needs the video:write scope or an admin session.","operationId":"getAdminVideoQueue","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"killSwitch":{"type":"boolean"},"spendUsed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"capPerVideo":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"capPerSite":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","killSwitch","spendUsed","capPerVideo","capPerSite"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/admin/voice":{"get":{"summary":"The voice-agent settings the call path uses, and whether a stored override or the site config answers them.","operationId":"getAdminVoice","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"provider":{"default":"telnyx","type":"string","enum":["telnyx","xai","elevenlabs"]},"voiceId":{"default":"","type":"string"},"speed":{"default":1,"type":"number","minimum":0.25,"maximum":2},"language":{"default":"auto","type":"string","minLength":1,"maxLength":20},"pronunciation":{"default":[],"maxItems":100,"type":"array","items":{"type":"object","properties":{"word":{"type":"string","minLength":1,"maxLength":100},"spoken":{"type":"string","minLength":1,"maxLength":200}},"required":["word","spoken"],"additionalProperties":false}},"keyterms":{"default":[],"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":50}},"welcomeEnabled":{"default":true,"type":"boolean"},"welcomeText":{"default":"","type":"string","maxLength":2000},"interruptible":{"default":true,"type":"boolean"},"followUpOnSilence":{"default":false,"type":"boolean"},"personaName":{"default":"","type":"string","maxLength":60}},"required":["provider","voiceId","speed","language","pronunciation","keyterms","welcomeEnabled","welcomeText","interruptible","followUpOnSilence","personaName"],"additionalProperties":false},"source":{"type":"string","enum":["default","override","invalid"]}},"required":["settings","source"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"put":{"summary":"Replace the stored voice-agent settings row; the schema must pass. Needs the settings:write scope or an admin session.","operationId":"putAdminVoice","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"default":"telnyx","type":"string","enum":["telnyx","xai","elevenlabs"]},"voiceId":{"default":"","type":"string"},"speed":{"default":1,"type":"number","minimum":0.25,"maximum":2},"language":{"default":"auto","type":"string","minLength":1,"maxLength":20},"pronunciation":{"default":[],"maxItems":100,"type":"array","items":{"type":"object","properties":{"word":{"type":"string","minLength":1,"maxLength":100},"spoken":{"type":"string","minLength":1,"maxLength":200}},"required":["word","spoken"]}},"keyterms":{"default":[],"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":50}},"welcomeEnabled":{"default":true,"type":"boolean"},"welcomeText":{"default":"","type":"string","maxLength":2000},"interruptible":{"default":true,"type":"boolean"},"followUpOnSilence":{"default":false,"type":"boolean"},"personaName":{"default":"","type":"string","maxLength":60}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"provider":{"default":"telnyx","type":"string","enum":["telnyx","xai","elevenlabs"]},"voiceId":{"default":"","type":"string"},"speed":{"default":1,"type":"number","minimum":0.25,"maximum":2},"language":{"default":"auto","type":"string","minLength":1,"maxLength":20},"pronunciation":{"default":[],"maxItems":100,"type":"array","items":{"type":"object","properties":{"word":{"type":"string","minLength":1,"maxLength":100},"spoken":{"type":"string","minLength":1,"maxLength":200}},"required":["word","spoken"],"additionalProperties":false}},"keyterms":{"default":[],"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":50}},"welcomeEnabled":{"default":true,"type":"boolean"},"welcomeText":{"default":"","type":"string","maxLength":2000},"interruptible":{"default":true,"type":"boolean"},"followUpOnSilence":{"default":false,"type":"boolean"},"personaName":{"default":"","type":"string","maxLength":60}},"required":["provider","voiceId","speed","language","pronunciation","keyterms","welcomeEnabled","welcomeText","interruptible","followUpOnSilence","personaName"],"additionalProperties":false},"source":{"type":"string","enum":["default","override","invalid"]}},"required":["settings","source"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"delete":{"summary":"Remove the stored voice-agent settings so the site config defaults apply again. Needs the settings:write scope or an admin session.","operationId":"deleteAdminVoice","parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}},"500":{"description":"An error on the server.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"A sentence for a person."},"code":{"type":"string","enum":["invalid_parameter","invalid_cursor","not_found","method_not_allowed","rate_limited","internal_error","unavailable"],"description":"Stable. Branch on this, not on `error`."},"details":{"description":"One entry per bad parameter, on `invalid_parameter`.","type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}},"required":["field","message"],"additionalProperties":false}}},"required":["error","code"],"additionalProperties":false},"Meta":{"type":"object","properties":{"attribution":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"text":{"type":"string","description":"Credit line to show next to the data."}},"required":["name","url","text"],"additionalProperties":false},"sources":{"type":"array","items":{"$ref":"#/components/schemas/Source"},"description":"Where the data comes from, with the license of each source."}},"required":["attribution","sources"],"additionalProperties":false},"Source":{"type":"object","properties":{"licenseUrl":{"type":["string","null"]},"attributionRequired":{"type":"boolean"},"key":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"license":{"type":"string","description":"The license of the data taken from this source."},"refresh":{"type":"string","description":"How often the source is re-imported."},"notes":{"type":["string","null"]}},"required":["licenseUrl","attributionRequired","key","name","url","license","refresh","notes"],"additionalProperties":false},"ListingItem":{"type":"object","properties":{"slug":{"type":"string","description":"The stable identifier. Use it in /listings/{slug}."},"name":{"type":"string"},"path":{"type":"string","description":"The listing page on this site."},"url":{"type":"string","description":"Absolute URL of the listing page."},"country":{"type":"string","description":"ISO 3166-1 alpha-2, upper case."},"region":{"type":"string"},"regionSlug":{"type":"string"},"city":{"type":"string"},"citySlug":{"type":"string"},"categories":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false},"description":"Primary category first."},"tier":{"type":"string"},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"completeness":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"0 to 100. How much of the record is filled in."},"attributes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Public facet attributes, keyed by attribute key."}},"required":["slug","name","path","url","country","region","regionSlug","city","citySlug","categories","tier","lat","lng","completeness","attributes"],"additionalProperties":false},"Listing":{"type":"object","properties":{"slug":{"type":"string","description":"The stable identifier. Use it in /listings/{slug}."},"name":{"type":"string"},"path":{"type":"string","description":"The listing page on this site."},"url":{"type":"string","description":"Absolute URL of the listing page."},"country":{"type":"string","description":"ISO 3166-1 alpha-2, upper case."},"region":{"type":"string"},"regionSlug":{"type":"string"},"city":{"type":"string"},"citySlug":{"type":"string"},"categories":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false},"description":"Primary category first."},"tier":{"type":"string"},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"completeness":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"0 to 100. How much of the record is filled in."},"attributes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Public facet attributes, keyed by attribute key."},"description":{"type":["string","null"]},"street":{"type":["string","null"]},"street2":{"type":["string","null"]},"postalCode":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"currency":{"type":"string","description":"ISO 4217."},"language":{"type":"string","description":"BCP 47."},"verified":{"type":"boolean","description":"The listing has an owner or a paid tier."},"source":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"license":{"type":"string"},"refresh":{"type":"string"},"notes":{"description":"The source note: how the record was gathered.","type":["string","null"]},"recordUrl":{"type":"string","description":"Where this record was read."},"retrieved":{"description":"When it was read, ISO 8601.","type":["string","null"]}},"required":["key","name","license","refresh","notes","recordUrl","retrieved"],"additionalProperties":false},{"type":"null"}],"description":"Null for a record that did not come from an import."},"published":{"description":"ISO 8601.","type":["string","null"]},"updated":{"type":"string","description":"ISO 8601."}},"required":["slug","name","path","url","country","region","regionSlug","city","citySlug","categories","tier","lat","lng","completeness","attributes","description","street","street2","postalCode","phone","website","currency","language","verified","source","published","updated"],"additionalProperties":false},"Page":{"type":"object","properties":{"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Listings that match the filters."},"nextCursor":{"description":"Null on the last page.","type":["string","null"]},"capped":{"type":"boolean","description":"True when more listings match than the cursor can reach. Add a filter or raise `limit`."},"fuzzy":{"type":"boolean","description":"True when nothing matched `q` exactly and the items are close matches."}},"required":["limit","total","nextCursor","capped","fuzzy"],"additionalProperties":false},"PlaceNode":{"type":"object","properties":{"kind":{"type":"string","enum":["country","region","city"]},"name":{"type":"string"},"slug":{"type":"string","description":"Pass a region slug as `region` and a city slug as `city` to /listings."},"country":{"type":"string","description":"ISO 3166-1 alpha-2 code. Pass it as `country` to /listings."},"url":{"type":"string","description":"The page for this place on the site."},"count":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Published listings in this place. A place with none is left out."},"children":{"type":"array","items":{"$ref":"#/components/schemas/PlaceNode"},"description":"Regions in a country, cities in a region. Empty for a city."}},"required":["kind","name","slug","country","url","count","children"],"additionalProperties":false},"CategoryNode":{"type":"object","properties":{"slug":{"type":"string","description":"Pass it as `category` to /listings."},"name":{"type":"string"},"url":{"type":"string","description":"The page for this category on the site."},"count":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Published listings in this category or one below it. Every category in the config is listed, also with 0."},"children":{"type":"array","items":{"$ref":"#/components/schemas/CategoryNode"}}},"required":["slug","name","url","count","children"],"additionalProperties":false},"Stats":{"type":"object","properties":{"listings":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Published listings."},"countries":{"type":"integer","minimum":0,"maximum":9007199254740991},"regions":{"type":"integer","minimum":0,"maximum":9007199254740991},"cities":{"type":"integer","minimum":0,"maximum":9007199254740991},"categories":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Every category in the tree, at any depth."},"sources":{"type":"integer","minimum":0,"maximum":9007199254740991},"lastUpdated":{"description":"When a published listing last changed. ISO 8601. Null with no listings.","type":["string","null"]}},"required":["listings","countries","regions","cities","categories","sources","lastUpdated"],"additionalProperties":false},"Product":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"category":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200},"brand":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":2000},"retailer":{"type":"string","enum":["amazon","etsy","other"]},"url":{"type":"string","maxLength":2000},"price_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"currency":{"type":"string"},"price_as_of":{"type":"string"},"image_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"image_alt":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"image_source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"images":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"alt":{"type":"string","minLength":1,"maxLength":300},"source":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["url","alt"],"additionalProperties":false}},"source":{"type":"string","minLength":1,"maxLength":300},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"featured":{"default":false,"type":"boolean"},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"page_url":{"type":"string","description":"This site's page for the product."},"buy_url":{"type":"string","description":"The outbound link, with this site's affiliate tag when it has one."}},"required":["slug","category","title","brand","description","retailer","url","price_cents","currency","price_as_of","images","source","tags","featured","rank","page_url","buy_url"],"additionalProperties":false},"ProductCategory":{"type":"object","properties":{"slug":{"type":"string","maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":500},"group":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"rank":{"default":0,"type":"integer","minimum":0,"maximum":9999},"product_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"page_url":{"type":"string"}},"required":["slug","name","description","rank","product_count","page_url"],"additionalProperties":false},"Certificate":{"type":"object","properties":{"code":{"type":"string","description":"The canonical code, XXXX-XXXX-XX."},"status":{"type":"string","enum":["valid","expired","revoked"],"description":"valid, expired or revoked. Computed when read."},"holder":{"type":"string","description":"The holder's name when they chose to show it, else Verified credential holder."},"courseTitle":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"issuedAt":{"type":"string","description":"ISO 8601."},"expiresAt":{"description":"ISO 8601, or null when the certificate does not expire.","type":["string","null"]},"issuer":{"type":"string","description":"The site that issued the certificate."}},"required":["code","status","holder","courseTitle","level","issuedAt","expiresAt","issuer"],"additionalProperties":false},"Course":{"type":"object","properties":{"slug":{"type":"string","description":"The stable identifier. Use it in /academy/courses/{slug}."},"title":{"type":"string"},"summary":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"durationMinutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"passMarkPercent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"maxAttempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"validityMonths":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Months a certificate stays valid. Null: it never expires."},"lessonCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"questionCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many exam questions the course has. Not the questions."},"path":{"type":"string","description":"The course page on this site."},"url":{"type":"string","description":"Absolute URL of the course page."},"updated":{"type":"string","description":"ISO 8601."}},"required":["slug","title","summary","level","durationMinutes","passMarkPercent","maxAttempts","validityMonths","lessonCount","questionCount","path","url","updated"],"additionalProperties":false},"CourseDetail":{"type":"object","properties":{"slug":{"type":"string","description":"The stable identifier. Use it in /academy/courses/{slug}."},"title":{"type":"string"},"summary":{"type":"string"},"level":{"type":"string","enum":["foundations","practitioner","specialist"]},"durationMinutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"passMarkPercent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"maxAttempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"validityMonths":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Months a certificate stays valid. Null: it never expires."},"lessonCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"questionCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many exam questions the course has. Not the questions."},"path":{"type":"string","description":"The course page on this site."},"url":{"type":"string","description":"Absolute URL of the course page."},"updated":{"type":"string","description":"ISO 8601."},"objectives":{"type":"array","items":{"type":"string"}},"faq":{"type":"array","items":{"type":"object","properties":{"q":{"type":"string"},"a":{"type":"string"}},"required":["q","a"],"additionalProperties":false}},"lessons":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"durationMinutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"path":{"type":"string"},"url":{"type":"string"}},"required":["slug","title","position","durationMinutes","path","url"],"additionalProperties":false}}},"required":["slug","title","summary","level","durationMinutes","passMarkPercent","maxAttempts","validityMonths","lessonCount","questionCount","path","url","updated","objectives","faq","lessons"],"additionalProperties":false}}}}