{
  "components": {
    "parameters": {
      "include": {
        "description": "A gated tier to include. It accepts only the two GATED tiers. custom_fields is tier 1 and always present (parent OD-27), so include=custom_fields is an unknown tier and returns 400. Declared for the read contract the follow-on ships (parent NG15).",
        "in": "query",
        "name": "include",
        "required": false,
        "schema": {
          "enum": [
            "contact_handles",
            "sensitive_traits"
          ],
          "type": "string"
        }
      }
    },
    "schemas": {
      "Contact": {
        "additionalProperties": false,
        "description": "The shared contact object. It is the webhook body's subject verbatim and the read contract's single-resource body. Every key of a caller's key set K appears exactly once, either here as a value or in missing as a reason, never both and never neither.",
        "properties": {
          "company": {
            "description": "The firmographic fields.",
            "properties": {
              "domain": {
                "description": "The registrable domain of the person's employer.",
                "examples": [
                  "northwindlogistics.com"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "employee_count": {
                "description": "The employee-count band of the person's employer, as delivered by the provider.",
                "examples": [
                  "201-500"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "est_revenue": {
                "description": "The revenue band of the person's employer, as delivered by the provider.",
                "examples": [
                  "$50M-$100M"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "industry": {
                "description": "The industry of the person's employer, as the resolving provider bands it.",
                "examples": [
                  "Transportation and Logistics"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "linkedin_url": {
                "description": "The organization's LinkedIn page. Emitted in the export for a company record.",
                "examples": [
                  "https://www.linkedin.com/company/northwind-logistics"
                ],
                "format": "uri",
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "The denormalized name of the person's employer.",
                "examples": [
                  "Northwind Logistics"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "website": {
                "description": "The company's website URL as delivered, kept alongside the normalized domain.",
                "examples": [
                  "https://northwindlogistics.com"
                ],
                "format": "uri",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "contract_version": {
            "const": "2026-09-04",
            "description": "The dated contract version."
          },
          "custom_fields": {
            "description": "The tenant's own custom fields. Tier 1, always present (parent OD-27).",
            "items": {
              "additionalProperties": false,
              "description": "One of the tenant's own custom fields and its value for this subject. Tier 1 (parent OD-27).",
              "properties": {
                "enriched_at": {
                  "description": "When an AI-populated value was written. It is the `enriched_at` member of a `custom_fields[]` entry, and it is set only when the source is ai_enrichment.",
                  "examples": [
                    "2026-09-01T04:22:18Z"
                  ],
                  "format": "date-time",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "key": {
                  "description": "The stable slug the tenant's field is addressed by, unique per tenant. It is the `key` member of a `custom_fields[]` entry and the key a secondary-resolver candidate alias matches against when the pipeline populates a value.",
                  "examples": [
                    "lifecycle_stage"
                  ],
                  "type": "string"
                },
                "label": {
                  "description": "The human-readable name of the tenant's field. It is the `label` member of a `custom_fields[]` entry.",
                  "examples": [
                    "Lifecycle stage"
                  ],
                  "type": "string"
                },
                "source": {
                  "description": "Where the value came from. It is the `source` member of a `custom_fields[]` entry and the per-field provenance carrier for tenant-defined fields.",
                  "enum": [
                    "ai_enrichment",
                    "inbound_mapping",
                    "manual"
                  ],
                  "examples": [
                    "inbound_mapping"
                  ],
                  "type": "string"
                },
                "type": {
                  "description": "The value type of the tenant's field, drawn from the thirteen-value type vocabulary this registry also seeds as its own rows. It is the `type` member of a `custom_fields[]` entry.",
                  "enum": [
                    "boolean",
                    "date",
                    "email",
                    "file",
                    "large_text",
                    "multi_select",
                    "number",
                    "phone",
                    "select",
                    "signature",
                    "text",
                    "textbox_list",
                    "url"
                  ],
                  "examples": [
                    "select"
                  ],
                  "type": "string"
                },
                "value": {
                  "description": "The stored value. Scalar types land in value_text; the four JSON storage types land in value_json."
                }
              },
              "required": [
                "enriched_at",
                "key",
                "label",
                "source",
                "type",
                "value"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "handles": {
            "description": "Email and phone handles. emails[] and phones[] are tier 2.",
            "properties": {
              "business_email": {
                "description": "The person's work email address, stored case-insensitively.",
                "examples": [
                  "dana.whitfield@northwindlogistics.com"
                ],
                "format": "email",
                "type": [
                  "string",
                  "null"
                ]
              },
              "emails": {
                "description": "Tier 2. Empty when the tier is not enabled.",
                "items": {
                  "additionalProperties": false,
                  "description": "One email handle.",
                  "properties": {
                    "kind": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "value": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "verified": {
                      "type": [
                        "boolean",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "value",
                    "kind",
                    "verified"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "phones": {
                "description": "Tier 2. Empty when the tier is not enabled.",
                "items": {
                  "additionalProperties": false,
                  "description": "One phone handle. callable = dnc_known && !dnc; a number whose callable is false is never emitted and appears in missing as withheld_compliance.",
                  "properties": {
                    "callable": {
                      "type": "boolean"
                    },
                    "dnc": {
                      "type": [
                        "boolean",
                        "null"
                      ]
                    },
                    "dnc_known": {
                      "type": "boolean"
                    },
                    "kind": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "value": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "value",
                    "kind",
                    "dnc",
                    "dnc_known",
                    "callable"
                  ],
                  "type": "object"
                },
                "type": "array"
              }
            },
            "type": "object"
          },
          "id": {
            "description": "The tenant-scoped identifier of the resolved person record.",
            "examples": [
              "9f2c1e4a-7b30-4a51-9d8e-2f6b1c0d5e77"
            ],
            "format": "uuid",
            "type": "string"
          },
          "identity": {
            "description": "The subject identity fields.",
            "properties": {
              "first_name": {
                "description": "The person's given name.",
                "examples": [
                  "Dana"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "full_name": {
                "description": "The person's full name, joined from the first and last name at ingest.",
                "examples": [
                  "Dana Whitfield"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "last_name": {
                "description": "The person's family name.",
                "examples": [
                  "Whitfield"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "linkedin_url": {
                "description": "The person's LinkedIn profile URL. Half of the row's unique key, so the provider upsert never overwrites it.",
                "examples": [
                  "https://www.linkedin.com/in/danawhitfield"
                ],
                "format": "uri",
                "type": [
                  "string",
                  "null"
                ]
              },
              "seniority": {
                "description": "The seniority band both providers return. Elected by the resolver today and landed by the winner writer this pull request ships.",
                "examples": [
                  "VP"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "title": {
                "description": "The person's job title at the resolved company.",
                "examples": [
                  "VP of Demand Generation"
                ],
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "links": {
            "description": "Absolute links to this subject.",
            "properties": {
              "portal": {
                "description": "The portal page for this subject.",
                "format": "uri",
                "type": [
                  "string",
                  "null"
                ]
              },
              "self": {
                "description": "The read-contract URL for this subject.",
                "format": "uri",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "location": {
            "description": "The subject location fields.",
            "properties": {
              "city": {
                "description": "The coarse business location the resolving provider reports. It is not the home address, which is a tier-3 trait.",
                "examples": [
                  "Columbus"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "postal_code": {
                "description": "The coarse postal code the resolving provider reports for the business location.",
                "examples": [
                  "43215"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "state": {
                "description": "The coarse business region the resolving provider reports.",
                "examples": [
                  "OH"
                ],
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "missing": {
            "additionalProperties": {
              "enum": [
                "no_source",
                "not_enriched",
                "unmapped",
                "withheld_compliance",
                "withheld_tier"
              ],
              "type": "string"
            },
            "description": "Every key in this caller's key set K that carries no value, mapped to one of the five closed reasons.",
            "type": "object"
          },
          "object": {
            "const": "contact",
            "description": "Always the literal \"contact\"."
          },
          "provenance": {
            "description": "How this subject was resolved and when it was seen.",
            "properties": {
              "first_seen_at": {
                "description": "When this person was first resolved for the tenant.",
                "examples": [
                  "2026-08-14T13:02:44Z"
                ],
                "format": "date-time",
                "type": "string"
              },
              "last_seen_at": {
                "description": "When this person was last re-resolved. Refreshed on every provider upsert.",
                "examples": [
                  "2026-09-04T18:59:02Z"
                ],
                "format": "date-time",
                "type": "string"
              },
              "name_source": {
                "description": "Which class of writer last set the name columns. The ADR-031 ladder ranks form above manual above enrichment above provider, and NULL ranks as provider.",
                "enum": [
                  "enrichment",
                  "form",
                  "manual",
                  "provider",
                  null
                ],
                "examples": [
                  "provider"
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "resolved_by": {
                "description": "Which vendor resolved this person. A per-ROW insert-only stamp, never per-field provenance, and masked to a tier label on every customer surface.",
                "examples": [
                  "secondary"
                ],
                "type": "string"
              }
            },
            "type": "object"
          },
          "subject_type": {
            "description": "Which catalogued subject this object describes.",
            "enum": [
              "company_profile",
              "contact",
              "person_profile"
            ],
            "type": "string"
          },
          "traits": {
            "description": "Tier 3 (sensitive_traits): the household, demographic and financial traits. The tier is available to every account and is off by default on each connector, so this array carries values only once the customer enables it on that connector. Until then it is empty and every tier-3 key is named in missing with the reason withheld_tier.",
            "items": {
              "additionalProperties": false,
              "description": "One normalized provider trait.",
              "properties": {
                "group": {
                  "enum": [
                    "demographics",
                    "financial",
                    "household"
                  ],
                  "type": "string"
                },
                "key": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "label": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "value": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "key",
                "label",
                "value",
                "group"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "object",
          "id",
          "subject_type",
          "contract_version",
          "identity",
          "company",
          "location",
          "handles",
          "traits",
          "custom_fields",
          "missing",
          "provenance",
          "links"
        ],
        "title": "Contact",
        "type": "object"
      },
      "ContactList": {
        "additionalProperties": false,
        "description": "The read contract list body. Declared now and produced by the follow-on (parent NG15), so push and pull cannot diverge before pull is built.",
        "properties": {
          "contract_version": {
            "const": "2026-09-04"
          },
          "data": {
            "items": {
              "$ref": "#/components/schemas/Contact"
            },
            "type": "array"
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "description": "An opaque keyset cursor.",
            "type": [
              "string",
              "null"
            ]
          },
          "object": {
            "const": "list"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "contract_version"
        ],
        "title": "ContactList",
        "type": "object"
      },
      "FieldPolicy": {
        "additionalProperties": false,
        "description": "The per-delivery accounting of what the caller received and what it did not. permitted_tiers is what the tenant may enable; enabled_tiers is what this integration has on.",
        "properties": {
          "enabled_tiers": {
            "items": {
              "enum": [
                1,
                2,
                3
              ],
              "type": "integer"
            },
            "type": "array"
          },
          "included": {
            "minimum": 0,
            "type": "integer"
          },
          "missing": {
            "minimum": 0,
            "type": "integer"
          },
          "missing_reason": {
            "additionalProperties": {
              "minimum": 0,
              "type": "integer"
            },
            "description": "A count per reason. Every key is one of the five closed reasons.",
            "propertyNames": {
              "enum": [
                "no_source",
                "not_enriched",
                "unmapped",
                "withheld_compliance",
                "withheld_tier"
              ]
            },
            "type": "object"
          },
          "permitted_tiers": {
            "items": {
              "enum": [
                1,
                2,
                3
              ],
              "type": "integer"
            },
            "type": "array"
          },
          "withheld_tiers": {
            "items": {
              "enum": [
                "contact_handles",
                "sensitive_traits"
              ],
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "permitted_tiers",
          "enabled_tiers",
          "withheld_tiers",
          "included",
          "missing",
          "missing_reason"
        ],
        "title": "FieldPolicy",
        "type": "object"
      },
      "WebhookEventV2": {
        "additionalProperties": false,
        "description": "The whole opt-in v2 outbound body. Its subject is a Contact; a whole body deliberately does NOT validate against Contact, because the envelope carries nine keys the subject does not.",
        "properties": {
          "classification": {
            "description": "Omitted when empty. The velocity-rule label.",
            "type": "string"
          },
          "contract_version": {
            "const": "2026-09-04"
          },
          "emitted_at": {
            "format": "date-time",
            "type": "string"
          },
          "event": {
            "const": "visitor.identified"
          },
          "field_policy": {
            "$ref": "#/components/schemas/FieldPolicy"
          },
          "id": {
            "description": "The visit_event id, and the X-Sight-Idempotency-Key value.",
            "type": "string"
          },
          "is_repeat": {
            "type": "boolean"
          },
          "served_from": {
            "description": "Omitted when empty. \"cache\" on a serve-local of a recognized return.",
            "type": "string"
          },
          "subject": {
            "$ref": "#/components/schemas/Contact"
          },
          "tier": {
            "enum": [
              "company",
              "person"
            ],
            "type": "string"
          },
          "visit": {
            "description": "The visit that produced this event.",
            "properties": {
              "captured_url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "domain_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "referrer": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "seen_at": {
                "format": "date-time",
                "type": [
                  "string",
                  "null"
                ]
              },
              "visitor_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "required": [
          "event",
          "contract_version",
          "id",
          "emitted_at",
          "tier",
          "is_repeat",
          "subject"
        ],
        "title": "WebhookEventV2",
        "type": "object"
      }
    },
    "securitySchemes": {
      "ApiKeyAuthBearer": {
        "description": "A tenant API key (`lsk_`-prefixed) presented as `Authorization: Bearer <key>`. A present-but-wrong or present-but-blank key answers 401 and never falls back to any other credential. Equivalent to ApiKeyAuthHeader; present exactly one of the two forms. The key is a secret: send it only from server-side code, never from a browser or a mobile app bundle.",
        "scheme": "bearer",
        "type": "http"
      },
      "ApiKeyAuthHeader": {
        "description": "A tenant API key presented as the `x-api-key` header. Equivalent to ApiKeyAuthBearer; present exactly one of the two forms.",
        "in": "header",
        "name": "x-api-key",
        "type": "apiKey"
      },
      "SourceTokenHeader": {
        "description": "The per-source inbound webhook token. This is the preferred form: use it whenever the sending platform can set a custom header. Equivalent to SourceTokenQuery; when both are present, this header is the one checked.",
        "in": "header",
        "name": "x-webhook-token",
        "type": "apiKey"
      },
      "SourceTokenQuery": {
        "description": "The per-source inbound webhook token as a query parameter. A fallback ONLY for platforms that cannot set a custom header: a URL that carries a token is routinely written to access, proxy and CDN logs, to browser history, and to the sending platform's own run history. Prefer SourceTokenHeader, never paste a token-bearing URL into a shared document or ticket, and rotate the token from the portal's Inbound Connections screen if such a URL is exposed. Equivalent to SourceTokenHeader, which takes precedence when both are present.",
        "in": "query",
        "name": "token",
        "type": "apiKey"
      }
    }
  },
  "info": {
    "contact": {
      "email": "support@ospry.ai",
      "name": "OSPRY Support"
    },
    "description": "Generated from packages/field-registry/src/registry.ts by `pnpm --filter @legion/field-registry exec tsx src/generate.ts`. Do not edit by hand. The read operations are declared as schemas and recorded in ADR-034; they are deliberately absent from `paths` until they exist, so a generated client can never call a route that returns 404.",
    "title": "OSPRY contact contract",
    "version": "2026-09-04"
  },
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "openapi": "3.1.0",
  "paths": {
    "/api/contacts/links": {
      "post": {
        "description": "For platforms that generate the link at send time. Names the contact by contactId, or both source and externalId (contactId takes precedence if both are sent). Requires the `contacts:links:write` scope, which is NOT granted by default to a new key (a key carrying only contacts:write is 403 here). A same-origin portal session is also accepted in place of a key; that first-party path is out of scope for this document.",
        "operationId": "mintContactLink",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "byContactId": {
                  "summary": "Selecting by contactId",
                  "value": {
                    "contactId": "3f9a6b12-4e21-4f2a-9c6d-1a2b3c4d5e6f",
                    "destinationUrl": "https://northwindlogistics.com/offer",
                    "type": "opaque"
                  }
                }
              },
              "schema": {
                "properties": {
                  "contactId": {
                    "description": "The contact's id, a UUID.",
                    "format": "uuid",
                    "maxLength": 128,
                    "minLength": 1,
                    "type": "string"
                  },
                  "destinationUrl": {
                    "description": "Must resolve to a domain this tenant has authorized.",
                    "maxLength": 2048,
                    "minLength": 1,
                    "type": "string"
                  },
                  "expSeconds": {
                    "description": "Only meaningful for type=signed.",
                    "maximum": 31536000,
                    "minimum": 1,
                    "type": "integer"
                  },
                  "externalId": {
                    "maxLength": 256,
                    "minLength": 1,
                    "type": "string"
                  },
                  "source": {
                    "maxLength": 64,
                    "minLength": 1,
                    "type": "string"
                  },
                  "type": {
                    "default": "opaque",
                    "enum": [
                      "opaque",
                      "signed"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "destinationUrl"
                ],
                "title": "MintLinkBody",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "minted": {
                    "summary": "An opaque link",
                    "value": {
                      "contactId": "3f9a6b12-4e21-4f2a-9c6d-1a2b3c4d5e6f",
                      "link": "https://northwindlogistics.com/offer?lsv=a.EXAMPLE-OPAQUE-LINK-TOKEN",
                      "ok": true,
                      "token": "a.EXAMPLE-OPAQUE-LINK-TOKEN",
                      "type": "opaque"
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "contactId": {
                      "format": "uuid",
                      "type": "string"
                    },
                    "link": {
                      "description": "The destination URL with the token appended as the lsv query parameter.",
                      "format": "uri",
                      "type": "string"
                    },
                    "ok": {
                      "const": true
                    },
                    "token": {
                      "description": "The lsv token value: a.<token> (opaque, the token a 43-character base64url string) or s.<keyId>.<claims>.<sig> (signed).",
                      "type": "string"
                    },
                    "type": {
                      "enum": [
                        "opaque",
                        "signed"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "contactId",
                    "type",
                    "token",
                    "link"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The minted token and its tagged link."
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "invalidDestination": {
                    "summary": "destinationUrl does not parse as a URL",
                    "value": {
                      "error": "invalid destination URL",
                      "ok": false
                    }
                  },
                  "malformedJson": {
                    "summary": "The body is not valid JSON",
                    "value": {
                      "error": "invalid JSON body",
                      "ok": false
                    }
                  },
                  "unauthorizedHost": {
                    "summary": "destinationUrl is not on an authorized domain",
                    "value": {
                      "error": "destination host is not authorized",
                      "ok": false
                    }
                  },
                  "validation": {
                    "summary": "Neither contactId nor (source, externalId) was provided",
                    "value": {
                      "error": "invalid payload",
                      "issues": [
                        {
                          "code": "custom",
                          "message": "Provide contactId or both source and externalId.",
                          "path": []
                        }
                      ],
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The failure envelope for this operation. issues is present only for a request-validation failure.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "issues": {
                      "description": "Present only for a request-validation failure.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Malformed JSON, a validation failure (issues carries the validation issues), an unauthorized destination host, or an invalid destination URL."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Unauthenticated",
                    "value": {
                      "error": "unauthorized",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The failure envelope for this operation. issues is present only for a request-validation failure.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "issues": {
                      "description": "Present only for a request-validation failure.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "No key and no portal session, or a present key that fails validation."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Missing scope",
                    "value": {
                      "error": "forbidden",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The failure envelope for this operation. issues is present only for a request-validation failure.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "issues": {
                      "description": "Present only for a request-validation failure.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The key is valid but lacks the contacts:links:write scope (not granted by default)."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Contact not found",
                    "value": {
                      "error": "contact not found",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The failure envelope for this operation. issues is present only for a request-validation failure.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "issues": {
                      "description": "Present only for a request-validation failure.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "No contact matches the given contactId, or the (source, externalId) pair."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Rate limited",
                    "value": {
                      "error": "rate limited",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The failure envelope for this operation. issues is present only for a request-validation failure.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "issues": {
                      "description": "Present only for a request-validation failure.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A per-customer fixed-window limit (120/min), or on the machine-auth path a per-key per-minute or daily limit; honor Retry-After.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the caller may retry.",
                "schema": {
                  "minimum": 1,
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Mint failed",
                    "value": {
                      "error": "mint failed",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The failure envelope for this operation. issues is present only for a request-validation failure.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "issues": {
                      "description": "Present only for a request-validation failure.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "An unclassified mint failure."
          },
          "503": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Signed links unavailable",
                    "value": {
                      "error": "signed links unavailable",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The failure envelope for this operation. issues is present only for a request-validation failure.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "issues": {
                      "description": "Present only for a request-validation failure.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "type=signed was requested but signed links are temporarily unavailable."
          }
        },
        "security": [
          {
            "ApiKeyAuthBearer": []
          },
          {
            "ApiKeyAuthHeader": []
          }
        ],
        "servers": [
          {
            "description": "The portal origin. Canonical for GET /api/v1/schema and POST /api/contacts/links. Deprecated dual-serve fallback for the push and webhook paths (prd-045-03), reachable there under different routes: POST /api/contacts mirrors POST /v1/contacts, and POST /api/contacts/webhook/{sourceId} mirrors POST /v1/contacts/{sourceId}. The HighLevel webhook is served by the edge host only. The origin push fallback accepts a portal session in place of an API key. It may answer 429 with Retry-After. Either origin fallback may answer 200 with a synchronous result instead of 202 when the durable queue is unavailable. The origin webhook fallback applies no rate limit.",
            "url": "https://app.ospry.ai"
          }
        ],
        "summary": "Mint a token and a tagged link for one contact.",
        "tags": [
          "Contacts"
        ],
        "x-required-scopes": [
          "contacts:links:write"
        ]
      }
    },
    "/api/v1/schema": {
      "get": {
        "description": "Unauthenticated and ungated: it carries the contract, never tenant data. It performs no tenant resolution and no database read.",
        "operationId": "getContractSchema",
        "responses": {
          "200": {
            "content": {
              "application/schema+json": {
                "examples": {
                  "trimmed": {
                    "summary": "A trimmed excerpt (the real document also declares ContactList, FieldPolicy and WebhookEventV2)",
                    "value": {
                      "$defs": {
                        "Contact": {
                          "properties": {
                            "id": {
                              "type": "string"
                            },
                            "subject_type": {
                              "type": "string"
                            }
                          },
                          "title": "Contact",
                          "type": "object"
                        }
                      },
                      "$id": "https://app.ospry.ai/api/v1/schema",
                      "$schema": "https://json-schema.org/draft/2020-12/schema",
                      "title": "OSPRY contact contract",
                      "x-ospry-contract-version": "2026-09-04"
                    }
                  }
                },
                "schema": {
                  "description": "A JSON Schema 2020-12 document.",
                  "type": "object"
                }
              }
            },
            "description": "The contract document. The handler has no failure branch: it performs no tenant resolution, calls no database, and always answers 200 with the committed schema bytes.",
            "headers": {
              "X-Ospry-Contract": {
                "description": "The dated contract version.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "servers": [
          {
            "description": "The portal origin. Canonical for GET /api/v1/schema and POST /api/contacts/links. Deprecated dual-serve fallback for the push and webhook paths (prd-045-03), reachable there under different routes: POST /api/contacts mirrors POST /v1/contacts, and POST /api/contacts/webhook/{sourceId} mirrors POST /v1/contacts/{sourceId}. The HighLevel webhook is served by the edge host only. The origin push fallback accepts a portal session in place of an API key. It may answer 429 with Retry-After. Either origin fallback may answer 200 with a synchronous result instead of 202 when the durable queue is unavailable. The origin webhook fallback applies no rate limit.",
            "url": "https://app.ospry.ai"
          }
        ],
        "summary": "Fetch the JSON Schema 2020-12 contract document.",
        "tags": [
          "Schema"
        ]
      }
    },
    "/v1/contacts": {
      "post": {
        "description": "Idempotent per account on (source, external_id): a re-pushed batch refreshes the existing rows rather than duplicating them. Requires the `contacts:write` scope. API access is a plan feature; keys are issued, rotated and revoked from the portal's Inbound Connections screen.",
        "operationId": "pushContacts",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "contactsShape": {
                  "summary": "The {contacts:[...]} shape",
                  "value": {
                    "contacts": [
                      {
                        "company": "Northwind Logistics",
                        "email": "jane.doe@northwindlogistics.com",
                        "external_id": "crm-88421",
                        "first_name": "Jane",
                        "last_name": "Doe",
                        "title": "VP Operations"
                      }
                    ]
                  }
                }
              },
              "schema": {
                "properties": {
                  "contacts": {
                    "items": {
                      "additionalProperties": {
                        "items": {},
                        "type": [
                          "string",
                          "number",
                          "boolean",
                          "array",
                          "object",
                          "null"
                        ]
                      },
                      "description": "One inbound record. Its keys are matched, case-insensitively and normalized, against the authenticating connection's field mappings; a mapped key lands on the contact's email, first_name, last_name, company or title, or on a tenant custom field, and an unmapped key is dropped, never guessed. A connection with no mappings falls back to the narrow six-field extraction: email, first_name, last_name, company, title, external_id. A nested array or object value is stored as its JSON string form; a null value is ignored (dropped, never stored).",
                      "type": "object"
                    },
                    "maxItems": 10000,
                    "minItems": 1,
                    "type": "array"
                  }
                },
                "required": [
                  "contacts"
                ],
                "title": "ContactsPushBody",
                "type": "object"
              }
            }
          },
          "description": "Accepts the narrow {contacts:[...]} shape. At most 10000 records per call (a larger batch is 413), and the whole body is capped at 8388608 bytes. A nested {events:[{resolution:{...}}]} shape some inbound connectors send is accepted only by the deprecated portal-origin fallback for this operation, never by this host; a body with no contacts array here is a 400.",
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "examples": {
                  "queued": {
                    "summary": "One record accepted and queued.",
                    "value": {
                      "accepted": 1,
                      "ok": true,
                      "queued": true
                    }
                  }
                },
                "schema": {
                  "description": "The async accept acknowledgment: the batch is durably queued, not yet upserted. Ingestion completes shortly after; a re-push is idempotent per account on (source, external_id).",
                  "properties": {
                    "accepted": {
                      "description": "The number of records accepted into this batch.",
                      "minimum": 0,
                      "type": "integer"
                    },
                    "ok": {
                      "const": true
                    },
                    "queued": {
                      "const": true
                    }
                  },
                  "required": [
                    "ok",
                    "accepted",
                    "queued"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "202 Accepted: the batch is durably queued and not yet upserted. Ingestion completes shortly after."
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "invalidPayload": {
                    "summary": "The body has no contacts array",
                    "value": {
                      "error": "invalid payload",
                      "ok": false
                    }
                  },
                  "malformedJson": {
                    "summary": "The body is not valid JSON",
                    "value": {
                      "error": "invalid JSON body",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Malformed JSON, or a body with no contacts array."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Unauthenticated",
                    "value": {
                      "error": "unauthorized",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The presented key is missing, unknown, malformed, expired, or past its revocation grace. The response never reveals which."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Missing scope",
                    "value": {
                      "error": "forbidden",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The key is not permitted to perform this operation (for example, it lacks the contacts:write scope)."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Unknown or inactive account",
                    "value": {
                      "error": "not found",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The {customerPublicId} subdomain is unknown or inactive."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "bodyTooLarge": {
                    "summary": "The body exceeds the byte cap",
                    "value": {
                      "error": "payload too large",
                      "ok": false
                    }
                  },
                  "tooManyContacts": {
                    "summary": "More than the max records were sent",
                    "value": {
                      "error": "too many contacts",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "More than 10000 records in one call, or a body over 8388608 bytes."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Rate limited",
                    "value": {
                      "error": "rate limited",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Too many requests for this API key: more than 120 in a 60-second window, or more than 50,000 in a 24-hour window. Wait the number of seconds in Retry-After, then retry: 60 for the short window, or the seconds left in the 24-hour window. Requests that fail authentication or routing do not count toward the limit.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the caller may retry.",
                "schema": {
                  "minimum": 1,
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Queue temporarily unavailable",
                    "value": {
                      "error": "temporarily unavailable",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The queue producer failed to accept the message. Retry is safe: the eventual upsert is idempotent."
          }
        },
        "security": [
          {
            "ApiKeyAuthBearer": []
          },
          {
            "ApiKeyAuthHeader": []
          }
        ],
        "servers": [
          {
            "description": "Canonical edge host. Serves the bearer push (POST /v1/contacts), the generic inbound webhook (POST /v1/contacts/{sourceId}) and the HighLevel webhook (POST /v1/contacts/highlevel). Not used for GET /api/v1/schema or POST /api/contacts/links, which the origin serves directly.",
            "url": "https://{customerPublicId}.lgnapi.com",
            "variables": {
              "customerPublicId": {
                "default": "replace-with-your-customer-public-id",
                "description": "Your tenant's opaque public id (a lowercase hex-and-hyphen UUID), exactly as it appears in the endpoint URLs the portal's Inbound Connections screen shows you. The host resolves the tenant from this label before any credential check runs."
              }
            }
          }
        ],
        "summary": "Push a batch of contacts (the canonical bearer-authenticated ingest).",
        "tags": [
          "Contacts"
        ],
        "x-required-scopes": [
          "contacts:write"
        ]
      }
    },
    "/v1/contacts/highlevel": {
      "post": {
        "description": "Authenticated by the connection's per-source token, sent as the ?token= query parameter of the webhook URL the portal shows (or the x-webhook-token header) and constant-time compared against the hash stored for the body's HighLevel location. A present token that does not match answers 401 in every case. The {customerPublicId} subdomain selects the account, and the body locationId (or location_id) must be an active HighLevel location connected to that account. Transition: until every connection has moved to the tokenized URL, a call that carries no token at all is still accepted and flagged to OSPRY; after the cutover it answers 401, so re-paste the webhook URL from the portal now. The HighLevel contact_id becomes external_id (source=highlevel), matching the idempotency anchor POST /v1/contacts and POST /v1/contacts/{sourceId} use.",
        "operationId": "pushHighLevelContact",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "highlevelContact": {
                  "summary": "A HighLevel contact.created payload",
                  "value": {
                    "companyName": "Northwind Logistics",
                    "contactId": "ghl_con_22190",
                    "email": "jane.doe@northwindlogistics.com",
                    "firstName": "Jane",
                    "lastName": "Doe",
                    "locationId": "ghl_loc_8841"
                  }
                }
              },
              "schema": {
                "additionalProperties": true,
                "description": "HighLevel sends snake_case and/or camelCase across event types; both spellings are accepted. Unknown properties are accepted but are not used for ingestion. contactId, contact_id or id must be present (400 otherwise). locationId or location_id must name an active HighLevel location connected to the account in the {customerPublicId} subdomain (404 otherwise).",
                "properties": {
                  "company": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "companyName": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "contactId": {
                    "type": "string"
                  },
                  "contact_id": {
                    "type": "string"
                  },
                  "email": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "firstName": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "first_name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "id": {
                    "description": "A HighLevel contact id, used as external_id when contactId/contact_id are absent.",
                    "type": "string"
                  },
                  "lastName": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "last_name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "locationId": {
                    "type": "string"
                  },
                  "location_id": {
                    "type": "string"
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "title": "HighLevelWebhookBody",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "examples": {
                  "queued": {
                    "summary": "The normalized contact accepted and queued.",
                    "value": {
                      "accepted": 1,
                      "ok": true,
                      "queued": true
                    }
                  }
                },
                "schema": {
                  "description": "The async accept acknowledgment: the batch is durably queued, not yet upserted. Ingestion completes shortly after; a re-push is idempotent per account on (source, external_id).",
                  "properties": {
                    "accepted": {
                      "description": "The number of records accepted into this batch.",
                      "minimum": 0,
                      "type": "integer"
                    },
                    "ok": {
                      "const": true
                    },
                    "queued": {
                      "const": true
                    }
                  },
                  "required": [
                    "ok",
                    "accepted",
                    "queued"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "202 Accepted: the batch is durably queued and not yet upserted. Ingestion completes shortly after."
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "malformedJson": {
                    "summary": "The body is not valid JSON",
                    "value": {
                      "error": "invalid JSON body",
                      "ok": false
                    }
                  },
                  "missingContactId": {
                    "summary": "Neither contactId, contact_id nor id was present",
                    "value": {
                      "error": "invalid payload",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Malformed JSON, or the body carries no contact id."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Missing or invalid per-source token",
                    "value": {
                      "error": "unauthorized",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A token was presented and does not match the stored hash for the body's HighLevel location (including a token that belongs to another connection or account), or, after the tokenized-URL cutover, no token was presented. The response never reveals which."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Unknown or unconfigured account",
                    "value": {
                      "error": "not found",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The {customerPublicId} subdomain is unknown or inactive, the account has no active HighLevel connection, or the body locationId is missing or is not an active HighLevel location of this account. Answered identically to an unknown subdomain, revealing nothing about whether HighLevel is configured."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Payload too large",
                    "value": {
                      "error": "payload too large",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A body over 8388608 bytes."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Rate limited",
                    "value": {
                      "error": "rate limited",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Too many requests for this HighLevel location: more than 120 in a 60-second window, or more than 50,000 in a 24-hour window. Wait the number of seconds in Retry-After, then retry: 60 for the short window, or the seconds left in the 24-hour window. Requests that fail authentication or routing do not count toward the limit.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the caller may retry.",
                "schema": {
                  "minimum": 1,
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Queue temporarily unavailable",
                    "value": {
                      "error": "temporarily unavailable",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The queue producer failed to accept the message. Retry is safe: the eventual upsert is idempotent."
          }
        },
        "security": [
          {
            "SourceTokenHeader": []
          },
          {
            "SourceTokenQuery": []
          }
        ],
        "servers": [
          {
            "description": "Canonical edge host. Serves the bearer push (POST /v1/contacts), the generic inbound webhook (POST /v1/contacts/{sourceId}) and the HighLevel webhook (POST /v1/contacts/highlevel). Not used for GET /api/v1/schema or POST /api/contacts/links, which the origin serves directly.",
            "url": "https://{customerPublicId}.lgnapi.com",
            "variables": {
              "customerPublicId": {
                "default": "replace-with-your-customer-public-id",
                "description": "Your tenant's opaque public id (a lowercase hex-and-hyphen UUID), exactly as it appears in the endpoint URLs the portal's Inbound Connections screen shows you. The host resolves the tenant from this label before any credential check runs."
              }
            }
          }
        ],
        "summary": "The HighLevel native inbound webhook.",
        "tags": [
          "Contacts"
        ]
      }
    },
    "/v1/contacts/{sourceId}": {
      "post": {
        "description": "Resolves the tenant by {sourceId} plus the presented per-source token, constant-time compared against the stored hash. Accepts the same {contacts:[...]} body as POST /v1/contacts; each record is mapped through this source's field mappings.",
        "operationId": "pushContactsBySource",
        "parameters": [
          {
            "description": "The per-source id of the connection, as shown in its webhook URL in the portal. Selects which per-source token and field-mapping set authenticate and shape this call.",
            "in": "path",
            "name": "sourceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "contactsShape": {
                  "summary": "The {contacts:[...]} shape",
                  "value": {
                    "contacts": [
                      {
                        "company": "Northwind Logistics",
                        "email": "jane.doe@northwindlogistics.com",
                        "external_id": "crm-88421",
                        "first_name": "Jane",
                        "last_name": "Doe",
                        "title": "VP Operations"
                      }
                    ]
                  }
                }
              },
              "schema": {
                "properties": {
                  "contacts": {
                    "items": {
                      "additionalProperties": {
                        "items": {},
                        "type": [
                          "string",
                          "number",
                          "boolean",
                          "array",
                          "object",
                          "null"
                        ]
                      },
                      "description": "One inbound record. Its keys are matched, case-insensitively and normalized, against the authenticating connection's field mappings; a mapped key lands on the contact's email, first_name, last_name, company or title, or on a tenant custom field, and an unmapped key is dropped, never guessed. A connection with no mappings falls back to the narrow six-field extraction: email, first_name, last_name, company, title, external_id. A nested array or object value is stored as its JSON string form; a null value is ignored (dropped, never stored).",
                      "type": "object"
                    },
                    "maxItems": 10000,
                    "minItems": 1,
                    "type": "array"
                  }
                },
                "required": [
                  "contacts"
                ],
                "title": "ContactsPushBody",
                "type": "object"
              }
            }
          },
          "description": "Accepts the narrow {contacts:[...]} shape. At most 10000 records per call (a larger batch is 413), and the whole body is capped at 8388608 bytes. A nested {events:[{resolution:{...}}]} shape some inbound connectors send is accepted only by the deprecated portal-origin fallback for this operation, never by this host; a body with no contacts array here is a 400.",
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "examples": {
                  "queued": {
                    "summary": "One record accepted and queued.",
                    "value": {
                      "accepted": 1,
                      "ok": true,
                      "queued": true
                    }
                  }
                },
                "schema": {
                  "description": "The async accept acknowledgment: the batch is durably queued, not yet upserted. Ingestion completes shortly after; a re-push is idempotent per account on (source, external_id).",
                  "properties": {
                    "accepted": {
                      "description": "The number of records accepted into this batch.",
                      "minimum": 0,
                      "type": "integer"
                    },
                    "ok": {
                      "const": true
                    },
                    "queued": {
                      "const": true
                    }
                  },
                  "required": [
                    "ok",
                    "accepted",
                    "queued"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "202 Accepted: the batch is durably queued and not yet upserted. Ingestion completes shortly after."
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "invalidPayload": {
                    "summary": "The body has no contacts array",
                    "value": {
                      "error": "invalid payload",
                      "ok": false
                    }
                  },
                  "malformedJson": {
                    "summary": "The body is not valid JSON",
                    "value": {
                      "error": "invalid JSON body",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Malformed JSON, or a body with no contacts array."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Unauthenticated",
                    "value": {
                      "error": "unauthorized",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The source id is unknown, inactive, not a webhook-type source, or the presented token does not match its stored hash. The response never reveals which."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Cross-tenant source",
                    "value": {
                      "error": "forbidden",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Edge only: the source belongs to a different tenant than the {customerPublicId} subdomain."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Unknown or inactive account",
                    "value": {
                      "error": "not found",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The {customerPublicId} subdomain is unknown or inactive."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "bodyTooLarge": {
                    "summary": "The body exceeds the byte cap",
                    "value": {
                      "error": "payload too large",
                      "ok": false
                    }
                  },
                  "tooManyContacts": {
                    "summary": "More than the max records were sent",
                    "value": {
                      "error": "too many contacts",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "More than 10000 records in one call, or a body over 8388608 bytes."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Rate limited",
                    "value": {
                      "error": "rate limited",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Too many requests for this source: more than 120 in a 60-second window, or more than 50,000 in a 24-hour window. Wait the number of seconds in Retry-After, then retry: 60 for the short window, or the seconds left in the 24-hour window. Requests that fail authentication or routing do not count toward the limit.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the caller may retry.",
                "schema": {
                  "minimum": 1,
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "examples": {
                  "failure": {
                    "summary": "Queue temporarily unavailable",
                    "value": {
                      "error": "temporarily unavailable",
                      "ok": false
                    }
                  }
                },
                "schema": {
                  "description": "The shared failure envelope every contacts endpoint returns on a non-2xx response.",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "ok": {
                      "const": false
                    }
                  },
                  "required": [
                    "ok",
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The queue producer failed to accept the message. Retry is safe: the eventual upsert is idempotent."
          }
        },
        "security": [
          {
            "SourceTokenHeader": []
          },
          {
            "SourceTokenQuery": []
          }
        ],
        "servers": [
          {
            "description": "Canonical edge host. Serves the bearer push (POST /v1/contacts), the generic inbound webhook (POST /v1/contacts/{sourceId}) and the HighLevel webhook (POST /v1/contacts/highlevel). Not used for GET /api/v1/schema or POST /api/contacts/links, which the origin serves directly.",
            "url": "https://{customerPublicId}.lgnapi.com",
            "variables": {
              "customerPublicId": {
                "default": "replace-with-your-customer-public-id",
                "description": "Your tenant's opaque public id (a lowercase hex-and-hyphen UUID), exactly as it appears in the endpoint URLs the portal's Inbound Connections screen shows you. The host resolves the tenant from this label before any credential check runs."
              }
            }
          }
        ],
        "summary": "The generic per-source inbound webhook (Zapier, Make, n8n, a custom script).",
        "tags": [
          "Contacts"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "Canonical edge host. Serves the bearer push (POST /v1/contacts), the generic inbound webhook (POST /v1/contacts/{sourceId}) and the HighLevel webhook (POST /v1/contacts/highlevel). Not used for GET /api/v1/schema or POST /api/contacts/links, which the origin serves directly.",
      "url": "https://{customerPublicId}.lgnapi.com",
      "variables": {
        "customerPublicId": {
          "default": "replace-with-your-customer-public-id",
          "description": "Your tenant's opaque public id (a lowercase hex-and-hyphen UUID), exactly as it appears in the endpoint URLs the portal's Inbound Connections screen shows you. The host resolves the tenant from this label before any credential check runs."
        }
      }
    },
    {
      "description": "The portal origin. Canonical for GET /api/v1/schema and POST /api/contacts/links. Deprecated dual-serve fallback for the push and webhook paths (prd-045-03), reachable there under different routes: POST /api/contacts mirrors POST /v1/contacts, and POST /api/contacts/webhook/{sourceId} mirrors POST /v1/contacts/{sourceId}. The HighLevel webhook is served by the edge host only. The origin push fallback accepts a portal session in place of an API key. It may answer 429 with Retry-After. Either origin fallback may answer 200 with a synchronous result instead of 202 when the durable queue is unavailable. The origin webhook fallback applies no rate limit.",
      "url": "https://app.ospry.ai"
    }
  ],
  "tags": [
    {
      "description": "The tenant-facing contact push, inbound webhook and link-minting operations.",
      "name": "Contacts"
    },
    {
      "description": "The machine-readable field contract.",
      "name": "Schema"
    }
  ],
  "webhooks": {
    "visitor.identified": {
      "post": {
        "description": "Sent to the customer endpoint configured on an integration whose payload_version selects v2. Signed with X-Sight-Signature over the exact bytes, keyed for idempotency on X-Sight-Idempotency-Key.",
        "operationId": "visitorIdentifiedV2",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEventV2"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The receiver accepted the delivery."
          }
        },
        "summary": "The opt-in v2 outbound webhook body."
      }
    }
  }
}
