> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dentalspace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Documentazione di dentalspace. Il testo completo, incluso il riferimento API, è in /llms-full.txt. Per endpoint, campi e codici di errore la fonte di verità è openapi.json: non usare endpoint o campi non documentati.

# Crea un lead

> Scope: `leads:write`.

Se esiste gia' un lead con lo stesso telefono (qualunque formato: si confronta quello normalizzato) o la stessa email, NON ne crea un altro: risponde 200 con quello esistente e `deduplicated: true`, senza modificarlo. Una chiamata da un numero sconosciuto e' un lead con `source: "phone"`. Una chiave limitata a delle sedi deve indicare `facilityId`.



## OpenAPI

````yaml /openapi.json post /leads
openapi: 3.1.0
info:
  title: DentalSpace API
  version: 1.0.0
  description: >-
    API pubblica di DentalSpace. Ogni richiesta porta `Authorization: Bearer
    <chiave>`

    (`dsk_live_…`, o `dsk_test_…` per una clinica di prova: nessun messaggio
    parte verso l'esterno).

    La chiave vede solo la sua clinica (e le sue sedi, se limitata): una risorsa
    di altri e' sempre 404.


    Errori: `{ "error": { "code", "message", "details", "requestId" } }`; `code`
    e' stabile, `message` no.

    Limiti: 100 richieste ogni 10 secondi e 20.000 al giorno (UTC) per clinica,
    60 e 12.000 per chiave;

    header `X-RateLimit-*` su ogni risposta, `Retry-After` sul 429 (che non
    consuma).

    Elenchi a cursore: `limit` 1-200, `cursor` = il `nextCursor` precedente,
    `updatedSince` per sincronizzare.

    `Idempotency-Key` sulle creazioni: la stessa chiave entro 24 ore ripete la
    prima risposta.
servers:
  - url: https://api.dentalspace.ai/v1
security:
  - bearerAuth: []
tags:
  - name: Meta
  - name: Studio
  - name: Pazienti
  - name: Agenda
  - name: Preventivi
  - name: Fatture e pagamenti
  - name: Documenti
  - name: Attività
  - name: Contatti commerciali
  - name: Messaggi WhatsApp
  - name: Telefonate
  - name: Agente vocale
  - name: Webhook ed eventi
paths:
  /leads:
    post:
      tags:
        - Contatti commerciali
      summary: Crea un lead
      description: >-
        Scope: `leads:write`.


        Se esiste gia' un lead con lo stesso telefono (qualunque formato: si
        confronta quello normalizzato) o la stessa email, NON ne crea un altro:
        risponde 200 con quello esistente e `deduplicated: true`, senza
        modificarlo. Una chiamata da un numero sconosciuto e' un lead con
        `source: "phone"`. Una chiave limitata a delle sedi deve indicare
        `facilityId`.
      operationId: createLead
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                firstName:
                  type: string
                  minLength: 1
                  maxLength: 100
                lastName:
                  type: string
                  maxLength: 100
                email:
                  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-z]{2,}$
                phone:
                  type: string
                  minLength: 3
                  maxLength: 40
                source:
                  default: other
                  type: string
                  enum:
                    - social_media
                    - website
                    - referral_link
                    - manual
                    - import
                    - phone
                    - walk_in
                    - other
                    - whatsapp
                sourceDetail:
                  type: string
                  maxLength: 200
                status:
                  default: new
                  type: string
                  enum:
                    - new
                    - contacted
                    - qualified
                    - proposal
                    - won
                    - lost
                notes:
                  type: string
                  maxLength: 5000
                assignedTo:
                  type: string
                  format: uuid
                  pattern: >-
                    ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
                estimatedValue:
                  type: number
                  minimum: 0
                  maximum: 100000000
                nextFollowUpAt:
                  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|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
                facilityId:
                  type: string
                  format: uuid
                  pattern: >-
                    ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
              required:
                - firstName
      responses:
        '201':
          description: Il lead creato (201) o quello gia' esistente (200).
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeadCreated'
        '400':
          description: 'Codici: `invalid_request`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'Codici: `unauthenticated`, `api_key_invalid`, `api_key_revoked`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Codici: `plan_required`, `insufficient_scope`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: 'Codici: `conflict`, `idempotency_in_progress`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'Codici: `idempotency_key_reused`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'Codici: `rate_limited`.'
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Codici: `internal_error`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: 'Codici: `unavailable`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - bearerAuth: []
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        maxLength: 255
      description: Facoltativa. La stessa chiave entro 24 ore ripete la prima risposta 2xx.
  headers:
    X-Request-Id:
      description: Identificativo della richiesta, da citare all'assistenza.
      schema:
        type: string
    X-RateLimit-Limit:
      description: Il tetto della finestra piu' stretta.
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Richieste rimaste nella finestra piu' stretta.
      schema:
        type: integer
    X-RateLimit-Reset:
      description: Quando riparte (secondi epoch, UTC).
      schema:
        type: integer
    Retry-After:
      description: Secondi da aspettare prima di riprovare.
      schema:
        type: integer
  schemas:
    LeadCreated:
      type: object
      properties:
        id:
          type: string
          format: uuid
          pattern: >-
            ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
        facilityId:
          anyOf:
            - type: string
              format: uuid
              pattern: >-
                ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
            - type: 'null'
        firstName:
          type: string
        lastName:
          anyOf:
            - type: string
            - type: 'null'
        email:
          anyOf:
            - type: string
            - type: 'null'
        phone:
          anyOf:
            - type: string
            - type: 'null'
        source:
          type: string
          enum:
            - social_media
            - website
            - referral_link
            - manual
            - import
            - phone
            - walk_in
            - other
            - whatsapp
        sourceDetail:
          anyOf:
            - type: string
            - type: 'null'
        status:
          type: string
          enum:
            - new
            - contacted
            - qualified
            - proposal
            - won
            - lost
        assignedTo:
          anyOf:
            - type: string
              format: uuid
              pattern: >-
                ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
            - type: 'null'
        estimatedValue:
          anyOf:
            - type: number
            - type: 'null'
        notes:
          anyOf:
            - type: string
            - type: 'null'
        nextFollowUpAt:
          anyOf:
            - type: string
            - type: 'null'
        lastContactedAt:
          anyOf:
            - type: string
            - type: 'null'
        lostReason:
          anyOf:
            - type: string
            - type: 'null'
        convertedPatientId:
          anyOf:
            - type: string
              format: uuid
              pattern: >-
                ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
            - type: 'null'
        convertedAt:
          anyOf:
            - type: string
            - type: 'null'
        createdAt:
          type: string
        updatedAt:
          type: string
        activities:
          description: Solo nel dettaglio.
          type: array
          items:
            $ref: '#/components/schemas/LeadActivity'
        deduplicated:
          description: 'true = c''era gia'': nessun lead creato, nessuna modifica.'
          type: boolean
      required:
        - id
        - facilityId
        - firstName
        - lastName
        - email
        - phone
        - source
        - sourceDetail
        - status
        - assignedTo
        - estimatedValue
        - notes
        - nextFollowUpAt
        - lastContactedAt
        - lostReason
        - convertedPatientId
        - convertedAt
        - createdAt
        - updatedAt
        - deduplicated
      additionalProperties: false
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              description: Codice stabile, da usare nel codice del client.
              type: string
              enum:
                - invalid_request
                - unauthenticated
                - api_key_invalid
                - api_key_revoked
                - forbidden
                - insufficient_scope
                - organization_suspended
                - plan_required
                - not_found
                - no_availability
                - conflict
                - idempotency_in_progress
                - idempotency_key_reused
                - rate_limited
                - internal_error
                - unavailable
            message:
              description: >-
                Spiegazione per una persona (in italiano). Non confrontarla nel
                codice.
              type: string
            details:
              anyOf:
                - type: object
                  propertyNames:
                    type: string
                  additionalProperties: {}
                - type: 'null'
            requestId:
              description: >-
                Lo stesso valore dell'header X-Request-Id: citalo
                all'assistenza.
              type: string
          required:
            - code
            - message
            - details
            - requestId
          additionalProperties: false
      required:
        - error
      additionalProperties: false
    LeadActivity:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
          enum:
            - call
            - email
            - whatsapp
            - meeting
            - note
        body:
          type: string
        at:
          description: Istante ISO 8601.
          type: string
      required:
        - id
        - kind
        - body
        - at
      additionalProperties: false
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: dsk_live_… / dsk_test_…

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.