openapi: 3.2.0 info: title: CoworkingView Leads API version: 1.0.0 description: 'Read API for the CoworkingView catalogue plus the lead write path. Within a major version this contract is APPEND-ONLY: fields are added, never removed or retyped, because clients include mobile builds that cannot be recalled. A breaking change means /v2, with /v1 kept alive for at least two app releases.' servers: - url: https://api.coworkingview.com description: Production - url: http://localhost:4000 description: Local development tags: - name: Leads paths: /v1/leads/contact: post: operationId: contact summary: General contact enquiry description: 'General contact enquiry (not tied to a specific space). Requires a Turnstile challenge token in production. Prefer inquiry when the user has already picked a listing. Safe to retry: send an `Idempotency-Key` header and a retried request with the SAME key and body replays the original response instead of creating a second lead; the same key with a DIFFERENT body returns 422 IDEMPOTENCY_KEY_CONFLICT. Omitting the header leaves behaviour unchanged.' tags: - Leads requestBody: required: true content: application/json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: name: type: string minLength: 1 maxLength: 120 email: type: string maxLength: 200 format: email pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ phone: type: string maxLength: 40 company: type: string maxLength: 160 message: type: string maxLength: 4000 locale: type: string enum: - en - de - es attribution: type: object properties: firstTouch: type: string maxLength: 200 lastTouch: type: string maxLength: 200 source: type: string enum: - web - app campaign: type: string maxLength: 120 viewedProperties: maxItems: 50 type: array items: type: string minLength: 1 maxLength: 200 pattern: ^[a-z0-9-]+$ required: - source challengeToken: type: string maxLength: 4000 website: type: string maxLength: 0 subject: type: string maxLength: 200 required: - name - email - locale responses: '200': description: Success content: application/json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: data: type: object properties: received: type: boolean const: true reference: type: string required: - received - reference additionalProperties: false meta: type: object properties: contractVersion: type: string pagination: type: object properties: page: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 pageSize: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 total: type: integer minimum: 0 maximum: 9007199254740991 pageCount: type: integer minimum: 0 maximum: 9007199254740991 required: - page - pageSize - total - pageCount additionalProperties: false truncated: type: boolean seed: type: integer minimum: -9007199254740991 maximum: 9007199254740991 required: - contractVersion additionalProperties: false required: - data - meta additionalProperties: false '400': description: VALIDATION_FAILED content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '403': description: CHALLENGE_FAILED content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '422': description: IDEMPOTENCY_KEY_CONFLICT content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '429': description: RATE_LIMITED content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '500': description: INTERNAL_ERROR content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '502': description: UPSTREAM_UNAVAILABLE content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false /v1/leads/inquiry: post: operationId: inquiry summary: Property inquiry (single property or shortlist) description: 'Create a lead for one space or a shortlist. This is the only write most agents need. Requires a Turnstile challenge token in production (CHALLENGE_FAILED if it is missing or invalid). CoworkingView forwards the enquiry to the operator; it does not broker the contract. Safe to retry: send an `Idempotency-Key` header (any unique string, e.g. a UUID per logical submission) and a retried request with the SAME key and body returns the original response without creating a second lead. The same key reused with a DIFFERENT body is a client bug and returns 422 IDEMPOTENCY_KEY_CONFLICT rather than guessing which body to honour. Omitting the header is fine — behaviour is unchanged and a request without one is executed every time.' tags: - Leads requestBody: required: true content: application/json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: name: type: string minLength: 1 maxLength: 120 email: type: string maxLength: 200 format: email pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ phone: type: string maxLength: 40 company: type: string maxLength: 160 message: type: string maxLength: 4000 locale: type: string enum: - en - de - es attribution: type: object properties: firstTouch: type: string maxLength: 200 lastTouch: type: string maxLength: 200 source: type: string enum: - web - app campaign: type: string maxLength: 120 viewedProperties: maxItems: 50 type: array items: type: string minLength: 1 maxLength: 200 pattern: ^[a-z0-9-]+$ required: - source challengeToken: type: string maxLength: 4000 website: type: string maxLength: 0 properties: minItems: 1 maxItems: 20 type: array items: type: string minLength: 1 maxLength: 200 pattern: ^[a-z0-9-]+$ desks: type: integer exclusiveMinimum: 0 maximum: 1000 moveInDate: type: string format: date 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])))$ required: - name - email - locale - properties responses: '200': description: Success content: application/json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: data: type: object properties: received: type: boolean const: true reference: type: string required: - received - reference additionalProperties: false meta: type: object properties: contractVersion: type: string pagination: type: object properties: page: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 pageSize: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 total: type: integer minimum: 0 maximum: 9007199254740991 pageCount: type: integer minimum: 0 maximum: 9007199254740991 required: - page - pageSize - total - pageCount additionalProperties: false truncated: type: boolean seed: type: integer minimum: -9007199254740991 maximum: 9007199254740991 required: - contractVersion additionalProperties: false required: - data - meta additionalProperties: false '400': description: VALIDATION_FAILED content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '403': description: CHALLENGE_FAILED content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '422': description: IDEMPOTENCY_KEY_CONFLICT content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '429': description: RATE_LIMITED content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '500': description: INTERNAL_ERROR content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false '502': description: UPSTREAM_UNAVAILABLE content: application/problem+json: schema: $schema: https://json-schema.org/draft/2020-12/schema type: object properties: type: type: string title: type: string status: type: integer minimum: 400 maximum: 599 detail: type: string code: type: string enum: - VALIDATION_FAILED - NOT_FOUND - RATE_LIMITED - CHALLENGE_FAILED - UPSTREAM_UNAVAILABLE - INTERNAL_ERROR - CLIENT_TOO_OLD - IDEMPOTENCY_KEY_CONFLICT errors: type: array items: type: object properties: path: type: string message: type: string required: - path - message additionalProperties: false traceId: type: string required: - type - title - status - code additionalProperties: false