{
  "openapi": "3.1.0",
  "info": {
    "title": "Jobin Integration API",
    "version": "1.0.0",
    "description": "Public Jobin OpenAPI contract for API-key authentication, contact enrichment, and sequence orchestration. Use the base URL below and append the documented path suffixes. Rate limits: API key validation 30/minute, reads 120/minute, writes 30/minute, and enrichment/campaign/message actions 10/minute per API identity."
  },
  "servers": [
    {
      "url": "https://my.jobin.cloud/api/openapi/v1",
      "description": "Public OpenAPI base URL. Append each path below to this base URL."
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Primary public authentication method. Create keys in Jobin.cloud under Workgroups > Integrations > Custom integration."
      }
    },
    "responses": {
      "RateLimitExceeded": {
        "description": "Rate limit exceeded. Wait for the retry-after header before sending the next request.",
        "headers": {
          "retry-after": {
            "description": "Seconds to wait before retrying.",
            "schema": { "type": "integer" }
          },
          "x-ratelimit-limit": {
            "description": "Maximum requests allowed in the current window.",
            "schema": { "type": "integer" }
          },
          "x-ratelimit-remaining": {
            "description": "Requests remaining in the current window.",
            "schema": { "type": "integer" }
          },
          "x-ratelimit-reset": {
            "description": "Seconds until the current window resets.",
            "schema": { "type": "integer" }
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/RateLimitError" }
          }
        }
      }
    },
    "schemas": {
      "ApiKeyValidationResponse": {
        "type": "object",
        "description": "Response returned when a Jobin API key is valid.",
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "Always true for accepted API keys."
          },
          "provider": {
            "type": "string",
            "description": "Human-readable provider or source label stored with the API key."
          },
          "label": {
            "type": "string",
            "description": "User-defined API key label shown in Jobin."
          },
          "workGroupId": {
            "type": "string",
            "description": "Authenticated Jobin workgroup identifier."
          },
          "userId": {
            "type": "string",
            "description": "Jobin user identifier that owns the API key."
          },
          "authScheme": {
            "type": "object",
            "properties": {
              "primary": {
                "type": "string",
                "description": "Header name integrations must use for public OpenAPI authentication."
              }
            },
            "description": "Authentication scheme supported by the public OpenAPI surface."
          },
          "rateLimitGuidance": {
            "type": "string",
            "description": "Operational guidance for public rate limits and retry behavior."
          },
          "sequenceGuidance": {
            "type": "string",
            "description": "Product guidance for campaign and sequence orchestration after validation."
          }
        }
      },
      "ApiKeyValidationErrorResponse": {
        "type": "object",
        "description": "Response returned when API-key validation fails.",
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "Always false for rejected validation attempts."
          },
          "error": {
            "type": "string",
            "description": "Human-readable validation failure reason."
          }
        }
      },
      "CurrentCampaign": {
        "type": "object",
        "description": "Current campaign or sequence assignment for the contact, when one is active.",
        "properties": {
          "campaignId": {
            "type": "string",
            "description": "Identifier of the campaign/sequence currently assigned to the contact."
          },
          "stagePositionCode": {
            "type": ["string", "null"],
            "description": "Current stage position code inside the campaign, when available."
          }
        }
      },
      "ConnectionLevel": {
        "type": "object",
        "description": "Connection level recorded for a Jobin user in the authenticated workgroup",
        "properties": {
          "jobinUser": {
            "type": "object",
            "description": "Jobin user whose sender account produced this connection level.",
            "properties": {
              "id": {
                "type": "string",
                "description": "Jobin user identifier."
              },
              "firstName": {
                "type": ["string", "null"],
                "description": "Jobin user's first name."
              },
              "lastName": {
                "type": ["string", "null"],
                "description": "Jobin user's last name."
              }
            }
          },
          "connectionLevel": {
            "type": ["string", "null"],
            "enum": ["1st", "2nd", "3rd", "4th", null],
            "description": "Connection level recorded by Jobin for this user"
          }
        }
      },
      "Location": {
        "type": "object",
        "description": "Public contact location. The backend uses the person location first and falls back to the current-role location for older records.",
        "properties": {
          "fullAddress": {
            "type": ["string", "null"],
            "description": "Full location string when the source only supplied one combined address."
          },
          "address": {
            "type": ["string", "null"],
            "description": "Street address when available."
          },
          "addressLine2": {
            "type": ["string", "null"],
            "description": "Second address line when available."
          },
          "city": {
            "type": ["string", "null"],
            "description": "City or locality."
          },
          "state": {
            "type": ["string", "null"],
            "description": "State or region label. This mirrors region for integrations that expect a state field."
          },
          "region": {
            "type": ["string", "null"],
            "description": "State, province, county, or region label."
          },
          "country": {
            "type": ["string", "null"],
            "description": "Country name."
          },
          "countryCode": {
            "type": ["string", "null"],
            "description": "Country code when normalization produced one."
          },
          "postalCode": {
            "type": ["string", "null"],
            "description": "Postal or ZIP code."
          }
        }
      },
      "Tag": {
        "type": "object",
        "description": "Jobin contact tag stored on the contact record.",
        "properties": {
          "_id": {
            "type": ["string", "null"],
            "description": "Internal tag identifier when present."
          },
          "value": {
            "type": ["string", "null"],
            "description": "Tag label shown in the Jobin UI."
          },
          "iconName": {
            "type": ["string", "null"],
            "description": "Optional UI icon name associated with the tag."
          },
          "color": {
            "type": ["string", "null"],
            "description": "Optional UI color associated with the tag."
          }
        },
        "additionalProperties": true
      },
      "CustomField": {
        "type": "object",
        "description": "Custom field value attached to the contact.",
        "properties": {
          "_id": {
            "type": ["string", "null"],
            "description": "Internal custom field value identifier when present."
          },
          "fieldName": {
            "type": ["string", "null"],
            "description": "Custom field name."
          },
          "value": {
            "type": ["string", "null"],
            "description": "Custom field value."
          }
        },
        "additionalProperties": true
      },
      "Period": {
        "type": "object",
        "description": "Start/end window for education, volunteering, or other profile history entries.",
        "properties": {
          "from": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "Start date when available."
          },
          "to": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "End date when available."
          },
          "isCurrent": {
            "type": ["boolean", "null"],
            "description": "Whether the period is currently active."
          }
        }
      },
      "OrganizationSummary": {
        "type": "object",
        "description": "Small public organization summary used by nested profile-history fields.",
        "properties": {
          "name": {
            "type": ["string", "null"],
            "description": "Organization name."
          },
          "socialUrl": {
            "type": ["string", "null"],
            "description": "Organization profile URL."
          },
          "companyWebsite": {
            "type": ["string", "null"],
            "description": "Organization website URL."
          },
          "employeeCountRange": {
            "type": ["string", "null"],
            "description": "Organization employee count range when available."
          }
        }
      },
      "Skill": {
        "type": "object",
        "description": "Enrichment skill attached to the contact.",
        "properties": {
          "value": {
            "type": ["string", "null"],
            "description": "Skill label."
          },
          "skillType": {
            "type": ["string", "null"],
            "description": "Optional skill category."
          },
          "level": {
            "type": ["integer", "null"],
            "description": "Optional Jobin skill level."
          },
          "topSkill": {
            "type": ["boolean", "null"],
            "description": "Whether the skill is marked as a top skill."
          }
        }
      },
      "Education": {
        "type": "object",
        "description": "Education entry exposed for candidate matching.",
        "properties": {
          "school": {
            "type": ["string", "null"],
            "description": "School or institution name."
          },
          "schoolSocialUrl": {
            "type": ["string", "null"],
            "description": "School profile URL when available."
          },
          "degreeLevel": {
            "type": ["string", "null"],
            "description": "Normalized degree level when Jobin has one."
          },
          "degrees": {
            "type": "array",
            "description": "Degree strings imported from profile data.",
            "items": { "type": "string" }
          },
          "fieldOfStudy": {
            "type": ["string", "null"],
            "description": "Primary field of study."
          },
          "majors": {
            "type": "array",
            "description": "Major fields of study imported from profile data.",
            "items": { "type": "string" }
          },
          "minors": {
            "type": "array",
            "description": "Minor fields of study imported from profile data.",
            "items": { "type": "string" }
          },
          "period": {
            "oneOf": [{ "$ref": "#/components/schemas/Period" }, { "type": "null" }],
            "description": "Education attendance period."
          }
        }
      },
      "Language": {
        "type": "object",
        "description": "Language entry attached to the contact.",
        "properties": {
          "value": {
            "type": ["string", "null"],
            "description": "Language name."
          },
          "level": {
            "type": ["integer", "null"],
            "description": "Optional Jobin language proficiency level."
          },
          "note": {
            "type": ["string", "null"],
            "description": "Optional note about the language."
          }
        }
      },
      "VolunteerExperience": {
        "type": "object",
        "description": "Volunteer experience entry attached to the contact.",
        "properties": {
          "roleTitle": {
            "type": ["string", "null"],
            "description": "Volunteer role title."
          },
          "organization": {
            "oneOf": [{ "$ref": "#/components/schemas/OrganizationSummary" }, { "type": "null" }],
            "description": "Volunteer organization."
          },
          "cause": {
            "type": ["string", "null"],
            "description": "Cause or category associated with the volunteer entry."
          },
          "description": {
            "type": ["string", "null"],
            "description": "Volunteer role description."
          },
          "location": {
            "oneOf": [{ "$ref": "#/components/schemas/Location" }, { "type": "null" }],
            "description": "Volunteer role location."
          },
          "period": {
            "oneOf": [{ "$ref": "#/components/schemas/Period" }, { "type": "null" }],
            "description": "Volunteer role period."
          }
        }
      },
      "Accomplishment": {
        "type": "object",
        "description": "Accomplishment, certification, award, or similar profile item attached to the contact.",
        "properties": {
          "value": {
            "type": ["string", "null"],
            "description": "Accomplishment title."
          },
          "type": {
            "type": ["string", "null"],
            "description": "Accomplishment type when categorized."
          },
          "issuer": {
            "type": ["string", "null"],
            "description": "Issuer or authority for the accomplishment."
          },
          "description": {
            "type": ["string", "null"],
            "description": "Longer accomplishment description."
          },
          "date": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "Date associated with the accomplishment."
          },
          "organization": {
            "oneOf": [{ "$ref": "#/components/schemas/OrganizationSummary" }, { "type": "null" }],
            "description": "Organization associated with the accomplishment."
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "Integration-friendly contact representation used by the public OpenAPI surface.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Jobin contact identifier."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the contact was created in Jobin."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the contact was last updated in Jobin."
          },
          "firstName": {
            "type": ["string", "null"],
            "description": "Contact first name."
          },
          "middleName": {
            "type": ["string", "null"],
            "description": "Contact middle name."
          },
          "lastName": {
            "type": ["string", "null"],
            "description": "Contact last name."
          },
          "workEmail": {
            "type": ["string", "null"],
            "description": "Primary work email when available."
          },
          "privateEmail": {
            "type": ["string", "null"],
            "description": "Primary private email when available."
          },
          "otherEmails": {
            "type": "array",
            "description": "Additional email addresses associated with the contact.",
            "items": { "type": "string" }
          },
          "workPhone": {
            "type": ["string", "null"],
            "description": "Primary work landline phone number when available."
          },
          "privatePhone": {
            "type": ["string", "null"],
            "description": "Primary private landline phone number when available."
          },
          "otherPhones": {
            "type": "array",
            "description": "Additional landline phone numbers associated with the contact.",
            "items": { "type": "string" }
          },
          "workMobilePhone": {
            "type": ["string", "null"],
            "description": "Primary work mobile phone number when available."
          },
          "privateMobilePhone": {
            "type": ["string", "null"],
            "description": "Primary private mobile phone number when available."
          },
          "otherMobilePhones": {
            "type": "array",
            "description": "Additional mobile phone numbers associated with the contact.",
            "items": { "type": "string" }
          },
          "socialUrls": {
            "type": "array",
            "description": "Social and professional profile URLs for the contact.",
            "items": { "type": "string" }
          },
          "currentTitle": {
            "type": ["string", "null"],
            "description": "Current role title from the contact's main professional experience."
          },
          "currentCompany": {
            "type": ["string", "null"],
            "description": "Current company name from the contact's main professional experience."
          },
          "currentCompanyFrom": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "Start date of the contact's current main professional experience when available."
          },
          "currentCompanyTo": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "End date stored on the contact's current main professional experience when available."
          },
          "currentCompanyTenureYears": {
            "type": ["number", "null"],
            "description": "Denormalized current-company tenure in years. Recruiters often call this tenure or years at current company."
          },
          "currentCompanyUrl": {
            "type": ["string", "null"],
            "description": "Current company website URL when available."
          },
          "currentCompanySocialUrl": {
            "type": ["string", "null"],
            "description": "Current company profile URL when available."
          },
          "location": {
            "oneOf": [{ "$ref": "#/components/schemas/Location" }, { "type": "null" }],
            "description": "Contact location shown in Jobin; falls back to current-role location when no person location exists."
          },
          "industry": {
            "type": ["string", "null"],
            "description": "industry stored on the contact."
          },
          "seniority": {
            "type": ["string", "null"],
            "description": "Normalized seniority/career level for the current role when available."
          },
          "managementLevel": {
            "type": ["string", "null"],
            "description": "Management level for the current role when available."
          },
          "previousTitle": {
            "type": ["string", "null"],
            "description": "Most recent previous role title when Jobin has work history."
          },
          "previousCompany": {
            "type": ["string", "null"],
            "description": "Most recent previous company name when Jobin has work history."
          },
          "previousCompanySocialUrl": {
            "type": ["string", "null"],
            "description": "Most recent previous company profile URL when available."
          },
          "headline": {
            "type": ["string", "null"],
            "description": "stored headline/title value."
          },
          "summary": {
            "type": ["string", "null"],
            "description": "summary, bio, or enriched profile summary."
          },
          "openProfile": {
            "type": ["boolean", "null"],
            "description": "if the profile is marked as open profile/open link."
          },
          "openToWork": {
            "type": ["boolean", "null"],
            "description": "if the contact is marked as open to work."
          },
          "connectionLevels": {
            "type": "array",
            "description": "All Jobin-user connection levels available inside the authenticated workgroup.",
            "items": { "$ref": "#/components/schemas/ConnectionLevel" }
          },
          "currentCampaign": {
            "oneOf": [{ "$ref": "#/components/schemas/CurrentCampaign" }, { "type": "null" }],
            "description": "Current Jobin campaign/sequence assignment for the contact."
          },
          "tags": {
            "type": "array",
            "description": "Tags attached to the contact in Jobin.",
            "items": { "$ref": "#/components/schemas/Tag" }
          },
          "skills": {
            "type": "array",
            "description": "Skills attached to the contact.",
            "items": { "$ref": "#/components/schemas/Skill" }
          },
          "education": {
            "type": "array",
            "description": "Education history attached to the contact.",
            "items": { "$ref": "#/components/schemas/Education" }
          },
          "experiences": {
            "type": "array",
            "description": "Full professional work history for single-contact detail reads. Contact list responses stay compact and may omit this field.",
            "items": { "$ref": "#/components/schemas/ContactExperience" }
          },
          "languages": {
            "type": "array",
            "description": "Languages attached to the contact.",
            "items": { "$ref": "#/components/schemas/Language" }
          },
          "volunteering": {
            "type": "array",
            "description": "Volunteer experience attached to the contact.",
            "items": { "$ref": "#/components/schemas/VolunteerExperience" }
          },
          "accomplishments": {
            "type": "array",
            "description": "Accomplishments, certifications, awards, or similar profile items attached to the contact.",
            "items": { "$ref": "#/components/schemas/Accomplishment" }
          },
          "customFieldList": {
            "type": "array",
            "description": "Custom field values attached to the contact.",
            "items": { "$ref": "#/components/schemas/CustomField" }
          },
          "source": {
            "type": ["string", "null"],
            "description": "Where the contact was sourced from."
          },
          "sourceType": {
            "type": ["string", "null"],
            "description": "Source category for the contact."
          },
          "unsubscribed": {
            "type": "boolean",
            "description": "Whether the contact is unsubscribed from outreach."
          }
        }
      },
      "ContactTask": {
        "type": "object",
        "description": "A native Jobin task associated with a contact.",
        "properties": {
          "id": { "type": "string", "description": "Task identifier." },
          "contactId": {
            "type": ["string", "null"],
            "description": "Associated contact identifier."
          },
          "title": { "type": "string" },
          "type": { "type": "string", "enum": ["task", "call", "meeting"] },
          "date": { "type": ["string", "null"], "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
          "time": { "type": ["string", "null"], "pattern": "^\\d{2}:\\d{2}$" },
          "description": { "type": ["string", "null"] },
          "links": { "type": "array", "items": { "type": "string" } },
          "location": { "type": ["string", "null"] },
          "phoneNumber": { "type": ["string", "null"] },
          "meetingLink": { "type": ["string", "null"] },
          "done": { "type": "boolean" },
          "flagged": { "type": "boolean" },
          "outcome": { "type": ["string", "null"] },
          "priority": { "type": ["string", "null"], "enum": ["low", "medium", "high", null] },
          "assigneeId": { "type": ["string", "null"] },
          "reminderEnabled": { "type": "boolean" },
          "reminderMinutesBefore": { "type": ["integer", "null"] },
          "source": { "type": "string" },
          "readOnly": { "type": "boolean" },
          "createdAt": { "type": ["string", "null"], "format": "date-time" },
          "updatedAt": { "type": ["string", "null"], "format": "date-time" }
        },
        "required": ["id", "title", "type", "done"]
      },
      "ContactTasksResponse": {
        "type": "object",
        "properties": {
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/ContactTask" } },
          "page": { "type": "integer" },
          "limit": { "type": "integer" },
          "count": { "type": "integer" }
        },
        "required": ["items", "page", "limit", "count"]
      },
      "ContactTaskCreateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["title", "type"],
        "properties": {
          "title": { "type": "string", "minLength": 1, "maxLength": 256 },
          "type": { "type": "string", "enum": ["task", "call", "meeting"] },
          "priority": { "type": "string", "enum": ["low", "medium", "high"] },
          "assigneeId": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" },
          "reminderEnabled": { "type": "boolean" },
          "reminderMinutesBefore": { "type": "integer", "minimum": 0, "maximum": 10080 },
          "date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
          "time": { "type": "string", "pattern": "^\\d{2}:\\d{2}$" },
          "description": { "type": "string", "maxLength": 4000 },
          "links": {
            "type": "array",
            "maxItems": 25,
            "items": { "type": "string", "format": "uri" }
          },
          "location": { "type": "string", "maxLength": 256 },
          "phoneNumber": { "type": "string", "maxLength": 256 },
          "meetingLink": { "type": "string", "format": "uri" },
          "queueId": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" },
          "jobspecId": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" },
          "pipelineId": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" },
          "stageName": { "type": "string", "maxLength": 256 },
          "subStageName": { "type": "string", "maxLength": 256 }
        }
      },
      "ContactExperience": {
        "type": "object",
        "description": "One professional experience from the contact's work history.",
        "properties": {
          "title": {
            "type": ["string", "null"],
            "description": "Role title for this professional experience."
          },
          "company": {
            "type": ["string", "null"],
            "description": "Company or organization name for this professional experience."
          },
          "from": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "Start date for this professional experience when available."
          },
          "to": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "End date for this professional experience when available."
          },
          "isCurrent": {
            "type": ["boolean", "null"],
            "description": "Whether this professional experience is marked as current."
          },
          "location": {
            "oneOf": [{ "$ref": "#/components/schemas/Location" }, { "type": "null" }],
            "description": "Role location for this professional experience when available."
          },
          "employmentType": {
            "type": ["string", "null"],
            "description": "Employment type for this professional experience, such as full-time or contract, when available."
          },
          "description": {
            "type": ["string", "null"],
            "description": "Responsibilities, achievements, or role description for this professional experience when available."
          }
        }
      },
      "ContactImportTagInput": {
        "oneOf": [
          {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Tag label. The backend normalizes string tags to the existing { value } tag shape."
          },
          {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "value": { "type": "string", "minLength": 1, "maxLength": 256 },
              "iconName": { "type": "string", "minLength": 1, "maxLength": 256 },
              "color": { "type": "string", "minLength": 1, "maxLength": 256 }
            },
            "required": ["value"]
          }
        ]
      },
      "ContactImportCustomFieldInput": {
        "type": "object",
        "additionalProperties": false,
        "description": "Custom field value accepted by contact import. Use fieldName for a free-text field; do not use name.",
        "anyOf": [{ "required": ["fieldName"] }, { "required": ["customFieldDefinitionId"] }],
        "properties": {
          "_id": {
            "type": "string",
            "pattern": "^[a-fA-F0-9]{24}$",
            "description": "Existing embedded custom-field value identifier when present."
          },
          "fieldName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Canonical name for a free-text custom field. The name property is not supported."
          },
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000,
            "description": "Text representation of the custom-field value."
          },
          "customFieldDefinitionId": {
            "type": "string",
            "pattern": "^[a-fA-F0-9]{24}$",
            "description": "Workgroup custom-field definition identifier for a typed field."
          },
          "valueType": {
            "type": "string",
            "enum": ["freeText", "number", "date", "list", "range"],
            "description": "Optional client-side type metadata; Jobin resolves the authoritative type from the workgroup definition."
          },
          "textValue": { "type": "string", "minLength": 1, "maxLength": 4000 },
          "numberValue": { "type": "number" },
          "dateValue": { "type": "string", "format": "date-time" },
          "listValue": { "type": "string", "minLength": 1, "maxLength": 4000 },
          "rangeValue": {
            "type": "object",
            "additionalProperties": false,
            "required": ["min", "max"],
            "properties": {
              "min": { "type": "number" },
              "max": { "type": "number" },
              "step": { "type": "number" },
              "unit": { "type": "string", "minLength": 1, "maxLength": 256 }
            }
          }
        }
      },
      "ContactImportContactInput": {
        "type": "object",
        "description": "Contact fields accepted by POST /contacts/import. Additional JSON properties are accepted and passed into the existing contact import path for supported Jobin contact fields.",
        "properties": {
          "_id": {
            "type": "string",
            "pattern": "^[a-fA-F0-9]{24}$",
            "description": "Optional existing contact id when the integration already knows the Jobin identifier."
          },
          "firstName": { "type": "string", "minLength": 1, "maxLength": 256 },
          "middleName": { "type": "string", "minLength": 1, "maxLength": 256 },
          "lastName": { "type": "string", "minLength": 1, "maxLength": 256 },
          "socialUrl": { "type": "string", "format": "uri", "maxLength": 2048 },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 320,
            "description": "Convenience email alias. When workEmail is omitted, the backend stores this as workEmail."
          },
          "workEmail": { "type": "string", "format": "email", "maxLength": 320 },
          "privateEmail": { "type": "string", "format": "email", "maxLength": 320 },
          "otherEmails": {
            "type": "array",
            "maxItems": 25,
            "items": { "type": "string", "format": "email", "maxLength": 320 }
          },
          "workPhone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Primary work landline phone number."
          },
          "privatePhone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Primary private landline phone number."
          },
          "otherPhones": {
            "type": "array",
            "maxItems": 25,
            "items": { "type": "string", "minLength": 1, "maxLength": 256 },
            "description": "Additional landline phone numbers."
          },
          "workMobilePhone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Primary work mobile phone number."
          },
          "privateMobilePhone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Primary private mobile phone number."
          },
          "otherMobilePhones": {
            "type": "array",
            "maxItems": 25,
            "items": { "type": "string", "minLength": 1, "maxLength": 256 },
            "description": "Additional mobile phone numbers."
          },
          "summary": { "type": "string", "minLength": 1, "maxLength": 4096 },
          "tags": {
            "type": "array",
            "maxItems": 50,
            "items": { "$ref": "#/components/schemas/ContactImportTagInput" }
          },
          "customFieldList": {
            "type": "array",
            "maxItems": 100,
            "description": "Custom-field values for this contact. Each item must use fieldName or customFieldDefinitionId; name is not supported.",
            "items": { "$ref": "#/components/schemas/ContactImportCustomFieldInput" }
          }
        },
        "additionalProperties": true
      },
      "ContactImportRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["contact"],
        "properties": {
          "contact": { "$ref": "#/components/schemas/ContactImportContactInput" },
          "notes": {
            "type": "array",
            "maxItems": 25,
            "items": { "type": "string", "minLength": 1, "maxLength": 4096 }
          },
          "autoUpdate": {
            "type": "boolean",
            "description": "When true, the existing import path schedules automatic enrichment/update behavior for the contact."
          },
          "overwriteMode": {
            "type": "string",
            "default": "askBeforeOverwriting",
            "enum": [
              "overwriteWithNewest",
              "alwaysKeepOriginalOnOverwrite",
              "askBeforeOverwriting"
            ],
            "description": "Duplicate-handling mode used by the existing import service."
          }
        }
      },
      "ContactImportResponse": {
        "type": "object",
        "properties": {
          "contactId": { "type": "string" },
          "autoUpdate": { "type": "boolean" },
          "overwriteMode": {
            "type": "string",
            "enum": ["overwriteWithNewest", "alwaysKeepOriginalOnOverwrite", "askBeforeOverwriting"]
          }
        }
      },
      "ContactBulkImportRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["contacts"],
        "properties": {
          "contacts": {
            "type": "array",
            "description": "One to fifty contact import requests. Each item follows POST /contacts/import semantics so dedupe, notes, tags, overwrite mode, and optional auto-update stay consistent.",
            "minItems": 1,
            "maxItems": 50,
            "items": { "$ref": "#/components/schemas/ContactImportRequest" }
          }
        }
      },
      "ContactBulkImportResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ContactImportResponse" }
          },
          "count": {
            "type": "integer",
            "description": "Number of contacts imported or deduplicated by the request."
          }
        }
      },
      "ContactTagMutationRequest": {
        "type": "object",
        "additionalProperties": false,
        "description": "Tag add/remove input. Provide tagId when using an existing Jobin tag, or value when the automation only knows the human-readable tag label.",
        "properties": {
          "tagId": {
            "type": "string",
            "pattern": "^[a-fA-F0-9]{24}$",
            "description": "Existing Jobin tag identifier."
          },
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Tag label to create/reuse for add operations, or remove by value for remove operations."
          },
          "iconName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Optional icon name used when creating a new tag by value."
          },
          "color": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Optional tag color/style used when creating a new tag by value."
          }
        },
        "anyOf": [{ "required": ["tagId"] }, { "required": ["value"] }]
      },
      "ContactTagMutationResponse": {
        "type": "object",
        "properties": {
          "modifiedCount": {
            "type": "integer",
            "description": "MongoDB modified count for the contact tag update."
          },
          "tag": { "$ref": "#/components/schemas/ContactTagSummary" },
          "removedTagIds": {
            "type": "array",
            "items": { "type": "string" }
          },
          "removedValues": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "ContactListResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Contact" }
          },
          "page": { "type": "integer" },
          "limit": { "type": "integer" },
          "nextCursor": {
            "type": ["string", "null"],
            "description": "Opaque cursor for the next page, or null when no more contacts are available."
          },
          "total": {
            "type": ["integer", "null"],
            "description": "Matching contact count. The response returns null unless includeTotal is true."
          }
        }
      },
      "PeopleSearchRequest": {
        "type": "object",
        "description": "People search request. Known profile filters provide precision, broad company/title/location filters provide scale, and Jobin caps one page at 50 results.",
        "additionalProperties": false,
        "properties": {
          "company": {
            "type": "object",
            "description": "Company-level filters such as name, current company, previous company, industry, employee range, funding, keywords, or headquarters filters.",
            "additionalProperties": true
          },
          "people": {
            "type": "object",
            "description": "Person-level filters such as name, profile URL, job title, level, function, connections, current company, previous company, tenure, skills, education, degree, school, field of study, job-description keywords, summary keywords, or minimum connections.",
            "additionalProperties": true
          },
          "locations": {
            "type": "array",
            "description": "Person location filters such as city, region, country code, sales region, ZIP/postal code, radius in km, radius around a city, or coordinates.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "similarTo": {
            "type": "object",
            "description": "Find more people like a supplied profile URL, known profile id, or example candidate.",
            "additionalProperties": true
          },
          "query": {
            "type": "string",
            "description": "Free-text fallback or broad search hint."
          },
          "cursor": {
            "type": ["string", "null"],
            "description": "Opaque cursor returned by the previous page."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "default": 10,
            "description": "Maximum people to return. Capped at 50."
          }
        }
      },
      "PeopleSearchProfile": {
        "type": "object",
        "description": "Contact-compatible profile result returned by people search. It intentionally omits CRM-only fields such as tags, notes, campaigns, custom fields, connection levels, unsubscribe state, events, and documents.",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string",
            "description": "Known profile identifier, not a CRM contact id."
          },
          "firstName": {
            "type": ["string", "null"],
            "description": "Person first name."
          },
          "lastName": {
            "type": ["string", "null"],
            "description": "Person last name."
          },
          "workEmail": {
            "type": ["string", "null"],
            "description": "Cached work email when already available on known profile data. People search does not enrich emails by default."
          },
          "privateEmail": {
            "type": ["string", "null"],
            "description": "Cached private email when already available on known profile data. People search does not enrich emails by default."
          },
          "socialUrls": {
            "type": "array",
            "description": "Social and professional profile URLs found for the person.",
            "items": { "type": "string" }
          },
          "currentTitle": {
            "type": ["string", "null"],
            "description": "Current role title mapped from experience data."
          },
          "currentCompany": {
            "type": ["string", "null"],
            "description": "Current company name mapped from experience data."
          },
          "currentCompanySocialUrl": {
            "type": ["string", "null"],
            "description": "Current company LinkedIn URL when available."
          },
          "location": {
            "oneOf": [{ "$ref": "#/components/schemas/Location" }, { "type": "null" }],
            "description": "Person location mapped from location data."
          },
          "headline": {
            "type": ["string", "null"],
            "description": "Public profile headline or current title."
          },
          "summary": {
            "type": ["string", "null"],
            "description": "Public profile summary/about text."
          },
          "skills": {
            "type": "array",
            "description": "Skills mapped from people search data.",
            "items": { "$ref": "#/components/schemas/Skill" }
          },
          "education": {
            "type": "array",
            "description": "Education mapped from people search data.",
            "items": { "$ref": "#/components/schemas/Education" }
          },
          "accomplishments": {
            "type": "array",
            "description": "Certifications or accomplishments mapped from people search data.",
            "items": { "$ref": "#/components/schemas/Accomplishment" }
          }
        }
      },
      "PeopleSearchResponse": {
        "type": "object",
        "description": "One page of people search results.",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/PeopleSearchProfile" }
          },
          "count": {
            "type": "integer",
            "description": "Number of results returned in this page."
          },
          "nextCursor": {
            "type": ["string", "null"],
            "description": "Cursor for the next page, or null when no more results are available."
          },
          "totalResults": {
            "type": ["integer", "null"],
            "description": "Total matching result count when available."
          }
        }
      },
      "ContactTagSummary": {
        "type": "object",
        "description": "Minimal contact tag metadata exposed for integration filtering.",
        "additionalProperties": false,
        "required": ["tagId", "value"],
        "properties": {
          "tagId": {
            "type": "string",
            "description": "Identifier to pass as tagIds when filtering contacts."
          },
          "value": {
            "type": "string",
            "description": "Tag label shown in Jobin."
          }
        }
      },
      "ContactTagListResponse": {
        "type": "object",
        "description": "All contact tags in the authenticated workgroup that match the optional name prefix.",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ContactTagSummary" }
          },
          "count": {
            "type": "integer",
            "description": "Number of tags returned."
          }
        }
      },
      "PatchContactResponse": {
        "type": "object",
        "properties": {
          "modifiedCount": { "type": "integer" },
          "contact": { "$ref": "#/components/schemas/Contact" }
        }
      },
      "UploadedFile": {
        "type": "object",
        "description": "Stored contact file metadata returned by the contact document endpoints.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Stored file identifier."
          },
          "name": {
            "type": "string",
            "description": "Stored file name."
          },
          "path": {
            "type": "string",
            "description": "Internal Jobin file path used by the storage-backed document download flow."
          },
          "mimetype": {
            "type": ["string", "null"],
            "description": "Detected MIME type for the uploaded file."
          },
          "extension": {
            "type": ["string", "null"],
            "description": "File extension without the leading dot."
          },
          "fileSize": {
            "type": ["integer", "null"],
            "description": "File size in bytes."
          }
        }
      },
      "ContactNoteRequest": {
        "type": "object",
        "description": "One recruiter-visible contact note.",
        "additionalProperties": false,
        "required": ["value"],
        "properties": {
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4096,
            "description": "Note body stored on the contact."
          }
        }
      },
      "ContactNoteResponse": {
        "type": "object",
        "description": "Identifier of the created or updated contact note.",
        "properties": {
          "noteId": {
            "type": "string",
            "description": "Stored note identifier."
          }
        }
      },
      "ContactDocumentUploadResponse": {
        "type": "object",
        "description": "Acknowledgement returned after Jobin stores a contact document.",
        "properties": {
          "message": {
            "type": "string",
            "description": "Upload result message."
          }
        }
      },
      "ContactEventParticipant": {
        "type": "object",
        "description": "A person, sender, or contact participating in a contact timeline event.",
        "properties": {
          "phone": { "type": "string" },
          "email": { "type": "string" },
          "socialUrl": {
            "type": "string",
            "description": "Social profile URL or identifier when the event came from a social channel."
          },
          "socialProfileId": {
            "type": "string",
            "description": "Provider-neutral social profile identifier when available."
          },
          "userId": { "type": "string" },
          "contactId": { "type": "string" },
          "avatar": { "type": "object", "additionalProperties": true }
        }
      },
      "ContactEventAttachment": {
        "type": "object",
        "description": "Attachment metadata for a contact timeline event.",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": ["string", "null"] },
          "mediaType": { "type": ["string", "null"] },
          "reference": { "type": ["string", "null"] },
          "byteSize": { "type": ["integer", "null"] }
        }
      },
      "ContactEventType": {
        "type": "object",
        "description": "Type metadata for a contact timeline event.",
        "properties": {
          "id": { "type": "string" },
          "value": { "type": "string" },
          "description": { "type": ["string", "null"] },
          "isInteraction": { "type": ["boolean", "null"] },
          "color": { "type": ["string", "null"] },
          "iconName": { "type": ["string", "null"] },
          "platform": { "type": ["string", "null"] }
        }
      },
      "ContactEvent": {
        "type": "object",
        "description": "One contact timeline event.",
        "properties": {
          "id": { "type": "string" },
          "from": {
            "oneOf": [
              { "$ref": "#/components/schemas/ContactEventParticipant" },
              { "type": "null" }
            ]
          },
          "to": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ContactEventParticipant" }
          },
          "subject": { "type": ["string", "null"] },
          "isSent": { "type": "boolean" },
          "content": { "type": ["string", "null"] },
          "attachments": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ContactEventAttachment" }
          },
          "participantsNr": { "type": ["integer", "null"] },
          "type": { "$ref": "#/components/schemas/ContactEventType" },
          "eventCreatedAt": { "type": "string", "format": "date-time" },
          "jobSpecId": { "type": ["string", "null"] }
        }
      },
      "ContactEventsResponse": {
        "type": "object",
        "description": "Downloaded contact timeline event batch. The batch can include email, SMS, social interactions, sequence activity, pipeline movement, enrichment or profile updates, and system events for the contact.",
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 20,
            "items": { "$ref": "#/components/schemas/ContactEvent" }
          },
          "count": {
            "type": "integer",
            "maximum": 20,
            "description": "Number of events returned in this response."
          }
        }
      },
      "EnrichmentRequest": {
        "type": "object",
        "description": "Selective enrichment request documented in the public guides.",
        "properties": {
          "findEmail": { "type": "boolean" },
          "findPhone": { "type": "boolean" },
          "findSocialUrls": { "type": "boolean" },
          "enrichCareerDetails": { "type": "boolean" },
          "userSocialUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Optional authenticated sender social profile URL to use for provider-specific enrichment context."
          },
          "emailPreference": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "mode": {
                "type": "string",
                "enum": ["preferably", "only"]
              },
              "target": {
                "type": "string",
                "enum": ["work", "private", "both"]
              }
            }
          }
        }
      },
      "EnrichmentResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "enriched",
              "alreadyEnriched",
              "freeEnrichment",
              "notFound",
              "error",
              "PROCESSING"
            ]
          },
          "reportId": { "type": ["string", "null"] },
          "enrichedData": { "type": ["object", "null"] }
        }
      },
      "EnrichmentReport": {
        "type": "object",
        "description": "Minimal enrichment report envelope returned by the reporting endpoints.",
        "properties": {
          "_id": { "type": "string" },
          "contactId": { "type": "string" },
          "userId": { "type": "string" },
          "workGroupId": { "type": "string" },
          "status": { "type": ["string", "null"] },
          "providerName": { "type": ["string", "null"] }
        },
        "additionalProperties": true
      },
      "EnrichmentReportListResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/EnrichmentReport" }
          }
        }
      },
      "Campaign": {
        "type": "object",
        "description": "Public sequence summary. The backend uses campaign ids for the same entities the UI labels as sequences.",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": ["string", "null"] },
          "isActive": { "type": "boolean" },
          "goal": { "type": ["string", "null"] },
          "dripCampaignArchived": { "type": "boolean" },
          "senderGroups": {
            "type": "array",
            "items": {}
          },
          "dripStageCount": { "type": "integer" },
          "contactCount": { "type": "integer" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "CampaignListResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Campaign" }
          },
          "page": { "type": "integer" },
          "limit": { "type": "integer" },
          "total": {
            "type": ["integer", "null"],
            "description": "Total matching campaigns when includeTotal=true; otherwise null."
          }
        }
      },
      "CampaignDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/Campaign" },
          {
            "type": "object",
            "properties": {
              "dripStages": {
                "type": "array",
                "items": {}
              },
              "operationsTimeframe": {
                "type": ["object", "null"]
              },
              "sequenceAnalysis": {
                "type": ["object", "null"]
              }
            }
          }
        ]
      },
      "CampaignAssignmentRequest": {
        "type": "object",
        "required": ["contactIds"],
        "properties": {
          "contactIds": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "description": "Contacts to assign in this request. Jobin accepts up to 50 contacts per campaign-assignment call.",
            "items": { "type": "string" }
          },
          "startPositionCode": { "type": "string", "default": "" }
        }
      },
      "CampaignAssignmentResponse": {
        "type": "object",
        "properties": {
          "accepted": { "type": "boolean" },
          "campaignId": { "type": "string" },
          "contactIds": {
            "type": "array",
            "items": { "type": "string" }
          },
          "jobGroupId": {
            "type": "string",
            "description": "Correlation id for listing outstanding operations created by this asynchronous assignment."
          },
          "queuedStagePositionCode": {
            "type": "string",
            "description": "The sequence stage position used for newly queued operations."
          }
        }
      },
      "JobinOperationSender": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["linkedin", "email", "sms"] },
          "id": { "type": "string", "description": "Sender URL for LinkedIn or sender ObjectId for email/SMS." }
        },
        "required": ["type", "id"],
        "additionalProperties": false
      },
      "JobinOperation": {
        "type": "object",
        "description": "Safe operational metadata for a persisted JobinJob. Worker payload, retry internals, and raw Mongo references are omitted.",
        "properties": {
          "id": { "type": "string" },
          "jobGroupId": { "type": "string" },
          "queue": { "type": "string", "enum": ["linkedin", "email", "sms", "jobin"] },
          "codename": { "type": "string" },
          "title": { "type": "string" },
          "status": { "type": "string", "enum": ["fail", "success", "pending", "processing", "blocked", "paused", "OUT_OF_TIMEFRAME", "THROTTLED", "SYNCHRONIZING", "COMPILING_SMART_FIELDS", "PENDING_HYDRATION", "UPSELL", "QUOTA_REACHED", "DISABLED"] },
          "contactId": { "type": "string" },
          "campaignId": { "type": "string" },
          "stagePositionCode": { "type": "string" },
          "sender": { "$ref": "#/components/schemas/JobinOperationSender" },
          "nextRunAt": { "type": ["string", "null"], "format": "date-time" },
          "throttledUntil": { "type": "string", "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        },
        "required": ["id", "queue", "codename", "title", "status", "createdAt", "updatedAt"],
        "additionalProperties": false
      },
      "OperationsListResponse": {
        "type": "object",
        "properties": {
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/JobinOperation" } },
          "page": { "type": "integer" },
          "limit": { "type": "integer" },
          "count": { "type": "integer", "description": "Number of items returned on this page." },
          "total": { "type": "integer", "description": "Exact number of matching persisted operations under the same workgroup, sender visibility, and filter rules, independent of page and limit." }
        },
        "required": ["items", "page", "limit", "count", "total"],
        "additionalProperties": false
      },
      "CampaignRemovalRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["contactIds"],
        "properties": {
          "contactIds": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "description": "Contacts to exit from this campaign. Contacts not currently assigned to the campaign are reported as skipped.",
            "items": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        }
      },
      "CampaignRemovalResponse": {
        "type": "object",
        "properties": {
          "accepted": { "type": "boolean" },
          "campaignId": { "type": "string" },
          "removedContactIds": {
            "type": "array",
            "items": { "type": "string" }
          },
          "skippedContactIds": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Requested contacts that were not currently assigned to this campaign."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "message": { "type": "string" }
        }
      },
      "RateLimitError": {
        "type": "object",
        "description": "Returned when a public OpenAPI rate limit is exceeded.",
        "properties": {
          "statusCode": { "type": "integer", "example": 429 },
          "error": { "type": "string", "example": "Too Many Requests" },
          "message": {
            "type": "string",
            "description": "Human-readable description of the limit that was exceeded."
          },
          "retryAfterSeconds": {
            "type": "integer",
            "description": "Seconds to wait before retrying the request."
          }
        }
      }
    }
  },
  "paths": {
    "/auth/validate": {
      "get": {
        "summary": "Validate API key",
        "description": "Validates a Jobin API key created in Workgroups > Integrations > Custom integration.",
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "API key accepted",
            "headers": {
              "Cache-Control": {
                "description": "Always `no-store` because the response contains API-key metadata.",
                "schema": { "type": "string" }
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiKeyValidationResponse" }
              }
            }
          },
          "400": {
            "description": "Malformed or missing API key",
            "headers": {
              "Cache-Control": {
                "description": "Always `no-store` because auth validation responses should not be cached.",
                "schema": { "type": "string" }
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiKeyValidationErrorResponse" }
              }
            }
          },
          "401": {
            "description": "Invalid API key",
            "headers": {
              "Cache-Control": {
                "description": "Always `no-store` because auth validation responses should not be cached.",
                "schema": { "type": "string" }
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiKeyValidationErrorResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/people/search": {
      "post": {
        "summary": "Search people",
        "description": "Searches known profile data for precision and a broad people index for scale, then returns contact-compatible profile results without creating CRM contacts. This sales-enabled feature returns a contact-sales authorization error when it is not enabled for the WorkGroup. Email and phone enrichment is not run for each result by default.",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PeopleSearchRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "People search results.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PeopleSearchResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts": {
      "get": {
        "summary": "List contacts",
        "description": "Lists contacts using the query parameters documented in the public contacts guide.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "description": "Filter contacts by work, private, or other email.",
            "schema": { "type": "string", "format": "email", "maxLength": 320 }
          },
          {
            "name": "firstName",
            "in": "query",
            "description": "Search contacts by structured first-name value. This uses the indexed contact search path and is more selective than legacy full-name matching.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 256 }
          },
          {
            "name": "lastName",
            "in": "query",
            "description": "Search contacts by structured last-name value. Combine with firstName when both values are known for the most selective contact lookup.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 256 }
          },
          {
            "name": "phone",
            "in": "query",
            "description": "Search contacts by work, private, mobile, or other phone number fields. The endpoint matches exact stored values, normalized E.164 variants when derivable, and digit-only partials. Pass the most specific number available; the value must contain at least 6 digits.",
            "schema": { "type": "string", "minLength": 6, "maxLength": 64 }
          },
          {
            "name": "socialUrl",
            "in": "query",
            "description": "Filter contacts by profile URL or identifier.",
            "schema": { "type": "string", "format": "uri", "maxLength": 2048 }
          },
          {
            "name": "campaignId",
            "in": "query",
            "description": "Filter contacts assigned to a campaign/sequence.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          },
          {
            "name": "tagIds",
            "in": "query",
            "description": "Filter contacts by one or more tag ids. Repeat the parameter or pass comma-separated ids. Contacts matching any supplied tag are returned.",
            "schema": {
              "type": "array",
              "items": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" },
              "maxItems": 50
            },
            "style": "form",
            "explode": false
          },
          {
            "name": "roleTitle",
            "in": "query",
            "description": "Filter contacts by current or previous role title text.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 256 }
          },
          {
            "name": "city",
            "in": "query",
            "description": "Filter contacts by city. Matches the contact's person location and role-level location fallback fields.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 256 }
          },
          {
            "name": "region",
            "in": "query",
            "description": "Filter contacts by state, province, county, or region. Matches the contact's person location and role-level location fallback fields.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 256 }
          },
          {
            "name": "country",
            "in": "query",
            "description": "Filter contacts by country name. Matches the contact's person location and role-level location fallback fields.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 256 }
          },
          {
            "name": "countryCode",
            "in": "query",
            "description": "Filter contacts by two-letter ISO country code, for example IT or US.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 2 }
          },
          {
            "name": "minYearsInCurrentRole",
            "in": "query",
            "description": "Minimum current-company tenure in years. Recruiters often call this tenure or years at current company.",
            "schema": { "type": "number", "minimum": 0, "maximum": 80 }
          },
          {
            "name": "maxYearsInCurrentRole",
            "in": "query",
            "description": "Maximum current-company tenure in years. Use 10 to exclude candidates who have been at the current company for more than ten years.",
            "schema": { "type": "number", "minimum": 0, "maximum": 80 }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor returned as nextCursor from a previous list response. Use this to continue beyond page 100.",
            "schema": { "type": "string", "maxLength": 512 }
          },
          {
            "name": "includeTotal",
            "in": "query",
            "description": "When true, include the matching contact count. The default is false, so ordinary list requests omit the count. Counting is more expensive than returning the page itself, so requests with includeTotal use a stricter 60 requests/minute limit and can take longer to complete. Only enable it when your integration actually uses the count.",
            "schema": { "type": "boolean", "default": false }
          },
          {
            "name": "page",
            "in": "query",
            "description": "One-based result page. Page-number pagination is capped at 100; use nextCursor as the cursor query parameter to continue beyond page 100.",
            "schema": { "type": "integer", "minimum": 1, "default": 1, "maximum": 100 }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum contacts to return per page.",
            "schema": { "type": "integer", "minimum": 1, "default": 25, "maximum": 100 }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged contacts",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactListResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/tags": {
      "get": {
        "summary": "List contact tags",
        "description": "Downloads contact tags in the authenticated workgroup. Use the optional name query parameter for prefix filtering before passing returned tagId values to GET /contacts?tagIds=...",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Optional case-sensitive tag name prefix. The value is trimmed and capped at 256 characters.",
            "schema": { "type": "string", "minLength": 1, "maxLength": 256 }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum tags to return. Defaults to 100 and is capped at 500 to keep free discovery requests bounded.",
            "schema": { "type": "integer", "minimum": 1, "default": 100, "maximum": 500 }
          }
        ],
        "responses": {
          "200": {
            "description": "Contact tags for the authenticated workgroup.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactTagListResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/import": {
      "post": {
        "summary": "Import contact",
        "description": "Imports one contact through the same normalization, tag creation, note creation, duplicate handling, and background update path used by Jobin imports.",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactImportRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Imported contact identifier and import options applied by the backend.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactImportResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/import/bulk": {
      "post": {
        "summary": "Bulk import contacts",
        "description": "Imports one to fifty vetted contacts through the same normalization, tag creation, note creation, duplicate handling, and background update path used by POST /contacts/import.",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactBulkImportRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Imported contact identifiers and import options applied by the backend.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactBulkImportResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}": {
      "get": {
        "summary": "Get contact",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Contact",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Contact" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      },
      "patch": {
        "summary": "Patch contact",
        "description": "Updates a contact using the same persistence path as the main app.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated contact payload",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PatchContactResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}/note": {
      "post": {
        "summary": "Create contact note",
        "description": "Creates one recruiter-visible note on an existing contact.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactNoteRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created contact note identifier.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactNoteResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}/note/{noteId}": {
      "post": {
        "summary": "Upsert contact note",
        "description": "Updates an existing recruiter note or creates it when the provided note id does not exist yet.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          },
          {
            "name": "noteId",
            "in": "path",
            "required": true,
            "description": "Jobin note identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactNoteRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created or updated contact note identifier.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactNoteResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}/tags": {
      "post": {
        "summary": "Add tag to contact",
        "description": "Creates or reuses a contact tag and applies it to one contact. This is intended for automation status tags such as outreach cohorts or reply states.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactTagMutationRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Applied tag and contact update count.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactTagMutationResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      },
      "delete": {
        "summary": "Remove tag from contact",
        "description": "Removes a tag from one contact by tag id or by tag value. The operation is scoped to the authenticated workgroup and contact.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactTagMutationRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removed tag ids or values and contact update count.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactTagMutationResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}/documents": {
      "get": {
        "summary": "List contact documents",
        "description": "Returns the contact's primary CV file first when present, followed by any other attachments stored on the contact.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        ],
        "responses": {
          "200": {
            "description": "Stored contact documents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/UploadedFile" }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      },
      "post": {
        "summary": "Upload contact document",
        "description": "Uploads a binary file for a contact. The uploaded file must be 10 MB or smaller. Types cv and resume replace the dedicated primary CV slot, while every other type is appended to attachments.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["file", "type"],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Binary file to upload. Maximum size: 10 MB."
                  },
                  "type": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256,
                    "description": "Document category. cv and resume replace the primary CV slot; every other value is stored as an attachment."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload acknowledgement.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactDocumentUploadResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}/events": {
      "get": {
        "summary": "Download contact events",
        "description": "Downloads a small batch from the contact's Jobin timeline. The timeline can include emails, SMS messages, social interactions, sequence activity, pipeline movement, enrichment or profile updates, and system events. Use beforeDate to page backward through older activity. This endpoint is limited to 10 requests per minute and returns at most 20 events per request.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Jobin contact identifier.",
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          },
          {
            "name": "beforeDate",
            "in": "query",
            "description": "Return events created before this timestamp.",
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum events to download. Capped at 20.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 20, "default": 10 }
          }
        ],
        "responses": {
          "200": {
            "description": "Downloaded contact timeline event batch",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactEventsResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}/tasks": {
      "get": {
        "summary": "List contact tasks",
        "description": "Lists tasks associated with one contact. Open tasks are returned by default.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          },
          {
            "name": "done",
            "in": "query",
            "description": "Whether to return completed tasks instead of open tasks.",
            "schema": { "type": "boolean", "default": false }
          },
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }
          }
        ],
        "responses": {
          "200": {
            "description": "Contact task page.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactTasksResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "Contact not found in the authenticated workgroup." },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      },
      "post": {
        "summary": "Create contact task",
        "description": "Creates a native Jobin task associated with the path contact. The contact association cannot be overridden in the request body.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactTaskCreateRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created contact task.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ContactTask" } }
            }
          },
          "400": { "description": "Invalid task fields." },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "Contact not found in the authenticated workgroup." },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/contacts/{contactId}/enrichment": {
      "post": {
        "summary": "Trigger selective enrichment",
        "description": "Starts selective contact enrichment. This endpoint can run asynchronously and will not always return the enrichment result directly in the initial response. After calling it, poll GET /contacts/{contactId} for up to 90 seconds to check whether the enriched contact data is ready.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/EnrichmentRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enrichment finished synchronously",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EnrichmentResponse" }
              }
            }
          },
          "202": {
            "description": "Enrichment accepted and still processing. Poll GET /contacts/{contactId} for up to 90 seconds to check whether the result is ready.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EnrichmentResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/operations": {
      "get": {
        "summary": "List Jobin operations",
        "description": "Lists safe operational metadata for persisted JobinJobs. Defaults to the exact persisted pending status. Results are limited to the authenticated workgroup and sender visibility rules. A missing pending operation does not prove that earlier work succeeded.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "status", "in": "query", "style": "form", "explode": true, "description": "Repeat to request multiple persisted JobinJob states. Defaults to pending.", "schema": { "type": "array", "items": { "type": "string", "enum": ["fail", "success", "pending", "processing", "blocked", "paused", "OUT_OF_TIMEFRAME", "THROTTLED", "SYNCHRONIZING", "COMPILING_SMART_FIELDS", "PENDING_HYDRATION", "UPSELL", "QUOTA_REACHED", "DISABLED"] }, "default": ["pending"] } },
          { "name": "codename", "in": "query", "style": "form", "explode": true, "description": "Repeat to filter by one or more operation codenames, such as sendLinkedinInvite.", "schema": { "type": "array", "items": { "type": "string" } } },
          { "name": "queue", "in": "query", "schema": { "type": "string", "enum": ["linkedin", "email", "sms", "jobin"] } },
          { "name": "jobGroupId", "in": "query", "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" } },
          { "name": "campaignId", "in": "query", "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" } },
          { "name": "contactId", "in": "query", "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" } },
          { "name": "userLinkedinUrl", "in": "query", "schema": { "type": "string", "format": "uri" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 25, "default": 25 } }
        ],
        "responses": {
          "200": { "description": "Bounded operations page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OperationsListResponse" }, "example": { "items": [{ "id": "aaaaaaaaaaaaaaaaaaaaaaaa", "jobGroupId": "bbbbbbbbbbbbbbbbbbbbbbbb", "queue": "linkedin", "codename": "sendLinkedinInvite", "title": "Send LinkedIn invitation", "status": "pending", "contactId": "cccccccccccccccccccccccc", "campaignId": "dddddddddddddddddddddddd", "sender": { "type": "linkedin", "id": "https://www.linkedin.com/in/sender" }, "createdAt": "2026-09-28T10:00:00.000Z", "updatedAt": "2026-09-28T10:00:00.000Z" }], "page": 1, "limit": 25, "count": 1, "total": 1 } } } },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/campaigns": {
      "get": {
        "summary": "List campaigns",
        "description": "Lists Jobin sequence records. The backend uses campaign ids for the same entities the UI labels as sequences.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "One-based result page. Page-number pagination is capped at 100.",
            "schema": { "type": "integer", "minimum": 1, "default": 1, "maximum": 100 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "default": 25, "maximum": 25 }
          },
          { "name": "isActive", "in": "query", "schema": { "type": "boolean" } },
          {
            "name": "includeArchived",
            "in": "query",
            "schema": { "type": "boolean", "default": false }
          },
          {
            "name": "includeTotal",
            "in": "query",
            "description": "When true, include the matching campaign count. The default is false to avoid unnecessary count work.",
            "schema": { "type": "boolean", "default": false }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged campaigns",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CampaignListResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/campaigns/{campaignId}": {
      "get": {
        "summary": "Get campaign",
        "description": "Returns a Jobin sequence detail record, including configured stages and analysis data.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign detail",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CampaignDetail" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    },
    "/campaigns/{campaignId}/contacts": {
      "post": {
        "summary": "Assign contacts to a campaign",
        "description": "Assigns contacts to a Jobin sequence asynchronously. The response includes jobGroupId for inspecting matching outstanding operations with GET /operations.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CampaignAssignmentRequest" },
              "example": {
                "contactIds": [],
                "startPositionCode": ""
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Assignment accepted",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CampaignAssignmentResponse" },
                "example": {
                  "accepted": true,
                  "campaignId": "aaaaaaaaaaaaaaaaaaaaaaaa",
                  "contactIds": ["bbbbbbbbbbbbbbbbbbbbbbbb"],
                  "jobGroupId": "cccccccccccccccccccccccc",
                  "queuedStagePositionCode": "first-touch"
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      },
      "delete": {
        "summary": "Remove contacts from a campaign",
        "description": "Exits contacts from a Jobin sequence, clearing pending sequence work and writing the standard campaign-exit audit event.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CampaignRemovalRequest" },
              "example": {
                "contactIds": []
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removal result",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CampaignRemovalResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" }
        }
      }
    }
  }
}
