{
  "openapi": "3.1.0",
  "info": {
    "title": "ZoeRelay Zapier API",
    "version": "1.1.0",
    "description": "Production OAuth 2.0 and workspace-scoped API facade used by the ZoeRelay Zapier integration.",
    "license": { "name": "Proprietary", "url": "https://zoerelay.com/terms" }
  },
  "servers": [{ "url": "https://zoerelay.com" }],
  "security": [{ "oauth2": [] }],
  "tags": [
    { "name": "OAuth", "description": "OAuth 2.0 authorization, token, revocation, and connected-profile endpoints." },
    { "name": "Contacts", "description": "Workspace-scoped contact search and mutation endpoints." },
    { "name": "Tickets", "description": "Workspace-scoped support ticket search and mutation endpoints." },
    { "name": "Conversations", "description": "Workspace-scoped conversation, messaging, assignment, status, and handoff endpoints." },
    { "name": "Tags", "description": "Apply and remove workspace conversation tags." },
    { "name": "Automations", "description": "Discover and run explicitly Zapier-enabled manual automations." },
    { "name": "REST Hooks", "description": "Subscribe, unsubscribe, and obtain bounded event samples for REST Hook triggers." },
    { "name": "Helpers", "description": "Bounded workspace option lists used by dynamic fields and hidden searches." }
  ],
  "paths": {
    "/oauth/authorize": {
      "get": { "operationId": "authorizeOAuth", "tags": ["OAuth"], "summary": "Start authorization", "security": [], "parameters": [{ "$ref": "#/components/parameters/ClientId" }, { "$ref": "#/components/parameters/RedirectUri" }, { "$ref": "#/components/parameters/State" }, { "name": "response_type", "in": "query", "required": true, "schema": { "const": "code" } }, { "name": "scope", "in": "query", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "Authorization context for an authenticated operator" }, "303": { "description": "Login or consent continuation" }, "400": { "$ref": "#/components/responses/Error" } } },
      "post": { "operationId": "decideOAuthConsent", "tags": ["OAuth"], "summary": "Allow or deny consent", "security": [{ "operatorSession": [] }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["decision"], "properties": { "decision": { "type": "string", "enum": ["allow", "deny"] }, "authorization_request": { "type": "string" }, "proof": { "type": "string" }, "shop_id": { "type": "string" } } } } } }, "responses": { "200": { "description": "Exact OAuth callback URL" }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" } } }
    },
    "/oauth/token": { "post": { "operationId": "exchangeOAuthToken", "tags": ["OAuth"], "summary": "Exchange or refresh tokens", "security": [], "requestBody": { "required": true, "content": { "application/x-www-form-urlencoded": { "schema": { "type": "object", "required": ["grant_type", "client_id", "client_secret"], "properties": { "grant_type": { "type": "string", "enum": ["authorization_code", "refresh_token"] }, "code": { "type": "string" }, "refresh_token": { "type": "string" }, "redirect_uri": { "type": "string", "format": "uri" }, "client_id": { "type": "string" }, "client_secret": { "type": "string", "writeOnly": true } } } } } }, "responses": { "200": { "description": "Opaque token response" }, "400": { "$ref": "#/components/responses/OAuthError" }, "401": { "$ref": "#/components/responses/OAuthError" } } } },
    "/oauth/revoke": { "post": { "operationId": "revokeOAuthToken", "tags": ["OAuth"], "summary": "Revoke a token family", "security": [], "requestBody": { "required": true, "content": { "application/x-www-form-urlencoded": { "schema": { "type": "object", "required": ["token", "client_id", "client_secret"], "properties": { "token": { "type": "string", "writeOnly": true }, "token_type_hint": { "type": "string" }, "client_id": { "type": "string" }, "client_secret": { "type": "string", "writeOnly": true } } } } } }, "responses": { "200": { "description": "Revocation accepted" }, "401": { "$ref": "#/components/responses/OAuthError" } } } },
    "/api/oauth/me": { "get": { "operationId": "getOAuthProfile", "tags": ["OAuth"], "summary": "Get connected user and workspace", "responses": { "200": { "description": "OAuth profile" }, "401": { "$ref": "#/components/responses/OAuthError" } } } },

    "/api/zapier/v1/contacts/search": { "get": { "operationId": "findContact", "tags": ["Contacts"], "summary": "Find contact", "parameters": [{ "name": "contact_id", "in": "query", "schema": { "type": "string" } }, { "name": "email", "in": "query", "schema": { "type": "string", "format": "email" } }, { "name": "phone", "in": "query", "schema": { "type": "string" } }], "responses": { "200": { "description": "Bounded contact matches" }, "400": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/contacts": { "post": { "operationId": "createContact", "tags": ["Contacts"], "summary": "Create contact", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "$ref": "#/components/requestBodies/ContactWrite" }, "responses": { "201": { "description": "Contact created" }, "409": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/contacts/{contactId}": { "patch": { "operationId": "updateContact", "tags": ["Contacts"], "summary": "Update contact", "parameters": [{ "$ref": "#/components/parameters/ContactId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "$ref": "#/components/requestBodies/ContactWrite" }, "responses": { "200": { "description": "Contact updated" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } },

    "/api/zapier/v1/tickets/search": { "get": { "operationId": "findTicket", "tags": ["Tickets"], "summary": "Find ticket", "parameters": [{ "name": "ticket_id", "in": "query", "schema": { "type": "string" } }, { "name": "contact_email", "in": "query", "schema": { "type": "string", "format": "email" } }], "responses": { "200": { "description": "Bounded ticket matches" }, "400": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/tickets": { "post": { "operationId": "createTicket", "tags": ["Tickets"], "summary": "Create ticket", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "$ref": "#/components/requestBodies/TicketWrite" }, "responses": { "201": { "description": "Ticket created" }, "409": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/tickets/{ticketId}": { "patch": { "operationId": "updateTicket", "tags": ["Tickets"], "summary": "Update ticket", "parameters": [{ "$ref": "#/components/parameters/TicketId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "$ref": "#/components/requestBodies/TicketWrite" }, "responses": { "200": { "description": "Ticket updated" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } },

    "/api/zapier/v1/conversations/search": { "get": { "operationId": "findConversation", "tags": ["Conversations"], "summary": "Find conversation", "parameters": [{ "name": "conversation_id", "in": "query", "schema": { "type": "string" } }, { "name": "contact_email", "in": "query", "schema": { "type": "string", "format": "email" } }, { "name": "external_reference", "in": "query", "schema": { "type": "string" } }], "responses": { "200": { "description": "Bounded conversation matches" }, "400": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/conversations": { "post": { "operationId": "createConversation", "tags": ["Conversations"], "summary": "Create conversation", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "201": { "description": "Conversation created" }, "409": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/conversations/{conversationId}/messages": { "post": { "operationId": "sendMessage", "tags": ["Conversations"], "summary": "Send message", "parameters": [{ "$ref": "#/components/parameters/ConversationId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "201": { "description": "Message accepted" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/conversations/{conversationId}/notes": { "post": { "operationId": "addInternalNote", "tags": ["Conversations"], "summary": "Add internal note", "parameters": [{ "$ref": "#/components/parameters/ConversationId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "201": { "description": "Internal note created" }, "404": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/conversations/{conversationId}/assignment": { "patch": { "operationId": "assignConversation", "tags": ["Conversations"], "summary": "Assign conversation", "parameters": [{ "$ref": "#/components/parameters/ConversationId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "200": { "description": "Conversation assigned" }, "404": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/conversations/{conversationId}/status": { "patch": { "operationId": "updateConversationStatus", "tags": ["Conversations"], "summary": "Update conversation status", "parameters": [{ "$ref": "#/components/parameters/ConversationId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "200": { "description": "Status updated" }, "404": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/conversations/{conversationId}/handoff": { "post": { "operationId": "requestHumanHandoff", "tags": ["Conversations"], "summary": "Request human handoff", "parameters": [{ "$ref": "#/components/parameters/ConversationId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "202": { "description": "Handoff requested" }, "404": { "$ref": "#/components/responses/Error" } } } },

    "/api/zapier/v1/tags/apply": { "post": { "operationId": "addTag", "tags": ["Tags"], "summary": "Add tag", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "200": { "description": "Tag applied" }, "404": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/tags/remove": { "post": { "operationId": "removeTag", "tags": ["Tags"], "summary": "Remove tag", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "200": { "description": "Tag removed" }, "404": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/automations/{automationId}/runs": { "post": { "operationId": "runAutomation", "tags": ["Automations"], "summary": "Run an enabled manual automation", "description": "The workflow must be active, published, manual, and configured with allowZapierInvocation=true.", "parameters": [{ "$ref": "#/components/parameters/AutomationId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "202": { "description": "Automation run accepted" }, "404": { "$ref": "#/components/responses/Error" } } } },

    "/api/zapier/v1/subscriptions": { "post": { "operationId": "subscribeRestHook", "tags": ["REST Hooks"], "summary": "Subscribe a REST Hook", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["event_type", "target_url"], "properties": { "event_type": { "$ref": "#/components/schemas/EventType" }, "target_url": { "type": "string", "format": "uri", "writeOnly": true }, "filters": { "type": "object", "additionalProperties": true } } } } } }, "responses": { "201": { "description": "Subscription created or reused" }, "400": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/subscriptions/{subscriptionId}": { "delete": { "operationId": "unsubscribeRestHook", "tags": ["REST Hooks"], "summary": "Disable a REST Hook subscription", "parameters": [{ "name": "subscriptionId", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "Subscription disabled" }, "404": { "$ref": "#/components/responses/Error" } } } },
    "/api/zapier/v1/events/samples/{eventType}": { "get": { "operationId": "listEventSamples", "tags": ["REST Hooks"], "summary": "List bounded event samples", "parameters": [{ "name": "eventType", "in": "path", "required": true, "schema": { "$ref": "#/components/schemas/EventType" } }], "responses": { "200": { "description": "Sample events with stable top-level IDs and UTC timestamps" }, "400": { "$ref": "#/components/responses/Error" } } } },

    "/api/zapier/v1/meta/users": { "$ref": "#/components/pathItems/ListUsers" },
    "/api/zapier/v1/meta/teams": { "$ref": "#/components/pathItems/ListTeams" },
    "/api/zapier/v1/meta/inboxes": { "$ref": "#/components/pathItems/ListInboxes" },
    "/api/zapier/v1/meta/tags": { "$ref": "#/components/pathItems/ListTags" },
    "/api/zapier/v1/meta/automations": { "$ref": "#/components/pathItems/ListAutomations" },
    "/api/zapier/v1/meta/contacts": { "$ref": "#/components/pathItems/ListContacts" },
    "/api/zapier/v1/meta/tickets": { "$ref": "#/components/pathItems/ListTickets" },
    "/api/zapier/v1/meta/conversations": { "$ref": "#/components/pathItems/ListConversations" }
  },
  "components": {
    "securitySchemes": {
      "oauth2": { "type": "oauth2", "flows": { "authorizationCode": { "authorizationUrl": "https://zoerelay.com/oauth/authorize", "tokenUrl": "https://zoerelay.com/oauth/token", "refreshUrl": "https://zoerelay.com/oauth/token", "scopes": { "contacts:read": "Read contacts", "contacts:write": "Create and update contacts", "conversations:read": "Read conversations", "conversations:write": "Create and update conversations", "tickets:read": "Read tickets", "tickets:write": "Create and update tickets", "automation:read": "Read enabled automations", "automation:write": "Run enabled automations", "profile:read": "Read the connected profile and workspace" } } } },
      "operatorSession": { "type": "http", "scheme": "bearer", "description": "ZoeRelay browser session used only for interactive consent." }
    },
    "parameters": {
      "ClientId": { "name": "client_id", "in": "query", "required": true, "schema": { "type": "string" } },
      "RedirectUri": { "name": "redirect_uri", "in": "query", "required": true, "schema": { "type": "string", "format": "uri" } },
      "State": { "name": "state", "in": "query", "required": true, "schema": { "type": "string" } },
      "IdempotencyKey": { "name": "Idempotency-Key", "in": "header", "required": true, "schema": { "type": "string", "maxLength": 190 }, "description": "Stable per-operation replay key. Same key and payload returns the durable result." },
      "ContactId": { "name": "contactId", "in": "path", "required": true, "schema": { "type": "string" } },
      "TicketId": { "name": "ticketId", "in": "path", "required": true, "schema": { "type": "string" } },
      "ConversationId": { "name": "conversationId", "in": "path", "required": true, "schema": { "type": "string" } },
      "AutomationId": { "name": "automationId", "in": "path", "required": true, "schema": { "type": "string" } }
    },
    "requestBodies": {
      "ContactWrite": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "phone": { "type": "string" }, "first_name": { "type": "string" }, "last_name": { "type": "string" }, "source": { "type": "string" } } } } } },
      "TicketWrite": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subject": { "type": "string", "maxLength": 160 }, "description": { "type": "string", "maxLength": 5000 }, "contact_id": { "type": "string" }, "status": { "type": "string", "enum": ["open", "pending", "waiting_customer", "resolved", "closed"] }, "priority": { "type": "string", "enum": ["low", "normal", "high", "urgent"] } } } } } }
    },
    "schemas": {
      "EventType": { "type": "string", "enum": ["contact.created", "contact.updated", "conversation.created", "message.incoming", "conversation.closed", "ticket.created", "ticket.updated", "conversation.handoff_requested", "tag.added", "csat.submitted", "automation.run_completed"] },
      "SafeError": { "type": "object", "required": ["error", "message"], "properties": { "error": { "type": "string" }, "message": { "type": "string" } } }
    },
    "responses": {
      "Error": { "description": "Safe workspace-scoped API error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SafeError" } } } },
      "OAuthError": { "description": "OAuth 2.0 error without credential material", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SafeError" } } } }
    },
    "pathItems": {
      "ListUsers": { "get": { "operationId": "findUsers", "tags": ["Helpers"], "summary": "List up to 100 workspace users", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } },
      "ListTeams": { "get": { "operationId": "listTeams", "tags": ["Helpers"], "summary": "List up to 100 active teams", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } },
      "ListInboxes": { "get": { "operationId": "findInboxes", "tags": ["Helpers"], "summary": "List up to 100 active inboxes", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } },
      "ListTags": { "get": { "operationId": "findTags", "tags": ["Helpers"], "summary": "List up to 100 tags", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } },
      "ListAutomations": { "get": { "operationId": "findAutomations", "tags": ["Helpers"], "summary": "List up to 100 Zapier-enabled automations", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } },
      "ListContacts": { "get": { "operationId": "listContactOptions", "tags": ["Helpers"], "summary": "List up to 100 recent contacts", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } },
      "ListTickets": { "get": { "operationId": "listTicketOptions", "tags": ["Helpers"], "summary": "List up to 100 recent tickets", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } },
      "ListConversations": { "get": { "operationId": "listConversationOptions", "tags": ["Helpers"], "summary": "List up to 100 recent conversations", "responses": { "200": { "description": "ID/name options" }, "401": { "$ref": "#/components/responses/OAuthError" }, "403": { "$ref": "#/components/responses/Error" } } } }
    }
  },
  "x-rate-limit": { "requests": 300, "windowSeconds": 60, "headers": ["RateLimit-Limit", "RateLimit-Remaining", "RateLimit-Reset"] },
  "x-idempotency": { "retentionDays": 30, "scope": "OAuth token family and operation" }
}
