{
  "openapi": "3.0.3",
  "info": {
    "title": "Alvio Campaign Intake API",
    "version": "1.1.0",
    "description": "Add or update one contact in an existing Alvio campaign. Create a campaign webhook source in Alvio first to obtain its campaign ID and x-api-key token. This focused contract is designed for Salesforce External Services and Flow."
  },
  "servers": [
    {
      "url": "https://www.alvio.ai"
    }
  ],
  "paths": {
    "/api/campaigns/webhook-receiver": {
      "post": {
        "operationId": "sendCampaignContact",
        "summary": "Send a contact to an Alvio campaign",
        "description": "The contact is matched by phone number and, when present, crm_contact_id. An active single-message campaign with an approved template can send immediately; sequence campaigns are processed asynchronously. A 200 response can contain nonzero errors or skipped counts, so inspect the response body.",
        "security": [
          {
            "CampaignApiKey": []
          }
        ],
        "parameters": [
          {
            "name": "campaign_id",
            "in": "query",
            "required": true,
            "description": "ID of the Alvio campaign that issued the API token.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignContactInput"
              },
              "example": {
                "phone_number": "+33612345678",
                "contact_name": "Jane Doe",
                "contact_email": "jane@example.com",
                "campaign": {
                  "source": "facebook",
                  "campaign_name": "summer_promo"
                },
                "crm_contact_id": "00Q000000000001",
                "salesforce_campaign_id": "701000000000001",
                "salesforce_campaign_member_id": "00v000000000001"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request processed. Inspect errors and skipped before treating the contact as accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignIntakeResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "description": "The request was processed but at least one contact failed in a campaign with retryable intake. Inspect errors before retrying.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignIntakeResult"
                }
              }
            }
          }
        }
      }
    },
    "/api/campaigns/{campaign_id}/contact-results": {
      "get": {
        "operationId": "listCampaignContactResults",
        "summary": "List contact results for a campaign",
        "description": "Returns execution state and the most recent message's delivery state for each contact. Results are sorted by contact creation time and ID. Poll pages again to see later status changes; this is a snapshot API, not an event stream.",
        "security": [
          {
            "CampaignApiKey": []
          }
        ],
        "parameters": [
          {
            "name": "campaign_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, from 1 to 100. Default 100.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Zero-based offset. Default 0.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of campaign contact results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignContactResultsPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "CampaignApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Per-campaign token shown once when the webhook source is created. Configure it as a custom header on the Salesforce Named Credential."
      }
    },
    "schemas": {
      "CampaignContactInput": {
        "type": "object",
        "required": [
          "phone_number"
        ],
        "properties": {
          "phone_number": {
            "type": "string",
            "description": "Recipient phone number. E.164 format is recommended.",
            "example": "+33612345678"
          },
          "contact_name": {
            "type": "string"
          },
          "contact_email": {
            "type": "string",
            "format": "email"
          },
          "campaign": {
            "$ref": "#/components/schemas/CampaignAttributionInput"
          },
          "crm_contact_id": {
            "type": "string",
            "description": "Stable Salesforce Lead or Contact ID. Used with the phone number to identify repeat deliveries."
          },
          "crm_deal_id": {
            "type": "string",
            "description": "Optional Salesforce Opportunity ID."
          },
          "salesforce_campaign_id": {
            "type": "string",
            "description": "Salesforce Campaign ID, stored as campaign contact metadata."
          },
          "salesforce_campaign_member_id": {
            "type": "string",
            "description": "Salesforce CampaignMember ID, stored as campaign contact metadata."
          },
          "lifecycle_stage": {
            "type": "string",
            "description": "Optional CRM lifecycle stage."
          }
        },
        "additionalProperties": true
      },
      "CampaignAttributionInput": {
        "type": "object",
        "description": "Optional acquisition campaign metadata, stored as custom_fields.campaign. The destination Alvio campaign is selected by the campaign_id query parameter.",
        "properties": {
          "source": {
            "type": "string",
            "description": "Acquisition source.",
            "example": "facebook"
          },
          "campaign_name": {
            "type": "string",
            "description": "Name of the acquisition campaign.",
            "example": "summer_promo"
          }
        }
      },
      "CampaignIntakeResult": {
        "type": "object",
        "required": [
          "success",
          "campaign_id",
          "inserted",
          "skipped",
          "errors",
          "existing_contacts",
          "sent_immediately"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "inserted": {
            "type": "integer"
          },
          "skipped": {
            "type": "integer"
          },
          "errors": {
            "type": "integer"
          },
          "existing_contacts": {
            "type": "integer"
          },
          "sent_immediately": {
            "type": "integer"
          }
        }
      },
      "CampaignContactResult": {
        "type": "object",
        "required": [
          "contact_id",
          "phone_number",
          "contact_status"
        ],
        "properties": {
          "contact_id": { "type": "string", "format": "uuid" },
          "phone_number": { "type": "string" },
          "crm_contact_id": { "type": "string", "nullable": true },
          "salesforce_campaign_id": { "type": "string", "nullable": true },
          "salesforce_campaign_member_id": { "type": "string", "nullable": true },
          "contact_status": { "type": "string" },
          "sequence_status": { "type": "string", "nullable": true },
          "progress_status": { "type": "string", "nullable": true },
          "delivery_status": {
            "type": "string",
            "nullable": true,
            "description": "State of the most recent campaign message: queued, sent, delivered, read, replied, failed, unknown, or null if no message record exists."
          },
          "created_at": { "type": "string", "format": "date-time", "nullable": true },
          "sent_at": { "type": "string", "format": "date-time", "nullable": true },
          "delivered_at": { "type": "string", "format": "date-time", "nullable": true },
          "read_at": { "type": "string", "format": "date-time", "nullable": true },
          "replied_at": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "CampaignContactResultsPage": {
        "type": "object",
        "required": ["campaign_id", "total", "limit", "offset", "results"],
        "properties": {
          "campaign_id": { "type": "string", "format": "uuid" },
          "total": { "type": "integer" },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" },
          "next_offset": { "type": "integer", "nullable": true },
          "results": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CampaignContactResult" }
          }
        }
      },
      "ApiError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "details": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "ApiError": {
        "description": "The request could not be processed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      }
    }
  }
}
