{"openapi":"3.0.0","paths":{"/v1/health":{"get":{"operationId":"HealthController_healthCheck","parameters":[],"responses":{"200":{"description":"API is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"timestamp":{"type":"string","example":"2026-01-26T12:00:00.000Z"}}}}}}},"summary":"Health check endpoint","tags":["Health"]}},"/v1/me":{"get":{"description":"Returns information about the authenticated API key and organization","operationId":"AuthController_getMe","parameters":[],"responses":{"200":{"description":"API key information","content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"message":{"type":"string","example":"Authenticated successfully"}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"Get current API key info","tags":["Authentication"]}},"/v1/contacts":{"get":{"description":"Returns a paginated list of contacts scoped to the workspaces accessible by the API key. Use the `workspaceIds` query parameter to narrow results to a subset of those workspaces.","operationId":"ContactController_list","parameters":[{"name":"workspaceIds","required":false,"in":"query","description":"Workspace IDs to narrow down results. Must be a subset of the workspaces accessible by the API key. Omit to return data from every workspace in scope. Accepts either a comma-separated string (`?workspaceIds=uuid1,uuid2`) — the documented format — or repeated array params (`?workspaceIds[]=uuid1&workspaceIds[]=uuid2`).","schema":{"example":"550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-indexed)","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":100,"default":10,"example":10,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search query to filter contacts by name, email, or company","schema":{"maxLength":100,"example":"john","type":"string"}}],"responses":{"200":{"description":"Paginated list of contacts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedPublicContactsDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"One or more requested workspaceIds are outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"List contacts","tags":["Contacts"]},"post":{"description":"Creates a new contact in your organization","operationId":"ContactController_create","parameters":[{"name":"x-workspace-id","in":"header","description":"Target workspace id (from `GET /v1/workspaces`). Required for multi-workspace API keys to designate which workspace to write to; omit for single-workspace keys, where it is resolved automatically. Must be within the API key scope.","required":false,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCreateContactDto"}}}},"responses":{"201":{"description":"The created contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"Create a contact","tags":["Contacts"]}},"/v1/contacts/{id}":{"get":{"description":"Returns a single contact by ID. Returns 404 if the contact is in a workspace outside the API key scope.","operationId":"ContactController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Contact not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get a contact","tags":["Contacts"]},"patch":{"description":"Updates an existing contact. Only provided fields will be updated. Returns 404 if the contact is in a workspace outside the API key scope.","operationId":"ContactController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicUpdateContactDto"}}}},"responses":{"200":{"description":"The updated contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Contact not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Update a contact","tags":["Contacts"]},"delete":{"description":"Soft-deletes a contact. The contact can no longer be retrieved after deletion. Returns 404 if the contact is in a workspace outside the API key scope.","operationId":"ContactController_delete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Contact deleted successfully"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Contact not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Delete a contact","tags":["Contacts"]}},"/v1/calls":{"get":{"description":"Returns a paginated list of calls scoped to the workspaces accessible by the API key. Use the `workspaceIds` query parameter to narrow results to a subset of those workspaces.","operationId":"CallController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (starts at 1)","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":100,"default":10,"example":10,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort as a JSON string, e.g. `?sort={\"orderBy\":\"createdAt\",\"order\":\"desc\"}` (URL-encoded). Defaults to `createdAt` descending.","schema":{"type":"string"}},{"name":"filters","required":false,"in":"query","description":"Filters as a JSON string. Each field wraps an operator object, e.g. `?filters={\"createdAt\":{\"op\":\"between\",\"from\":\"2026-06-01\",\"to\":\"2026-06-30\"}}` or `{\"createdAt\":{\"op\":\"gt\",\"from\":\"2026-06-25\"}}` (URL-encoded).","schema":{"type":"string"}},{"name":"workspaceIds","required":false,"in":"query","description":"Workspace IDs to narrow down results. Must be a subset of the workspaces accessible by the API key. Omit to return data from every workspace in scope. Accepts either a comma-separated string (`?workspaceIds=uuid1,uuid2`) — the documented format — or repeated array params (`?workspaceIds[]=uuid1&workspaceIds[]=uuid2`).","schema":{"example":"550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8","type":"string"}}],"responses":{"200":{"description":"Paginated list of calls","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedCallDTO"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"One or more requested workspaceIds are outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"List calls","tags":["Calls"]}},"/v1/calls/{id}":{"get":{"description":"Returns a single call by ID. Returns 404 if the call is in a workspace outside the API key scope.","operationId":"CallController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallWithContactDTO"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Call not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get a call","tags":["Calls"]}},"/v1/calls/{id}/recording":{"get":{"description":"Returns the recording metadata for a call. Returns 404 if the call is in a workspace outside the API key scope.","operationId":"CallController_getRecording","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The call recording metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCallRecordingDTO"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Call or recording not found, or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get call recording","tags":["Calls"]}},"/v1/calls/{id}/transcription":{"get":{"description":"Returns the transcription for a call. Returns 404 if the call is in a workspace outside the API key scope.","operationId":"CallController_getTranscription","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The call transcription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCallTranscriptionDTO"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Call or transcription not found, or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get call transcription","tags":["Calls"]}},"/v1/users":{"get":{"description":"Returns a paginated list of users (organization members) who have access to at least one of the workspaces accessible by the API key. Use the `organizationMembershipId` to create sequences. Use the `workspaceIds` query parameter to narrow results to a subset of those workspaces.","operationId":"UserController_list","parameters":[{"name":"workspaceIds","required":false,"in":"query","description":"Workspace IDs to narrow down results. Must be a subset of the workspaces accessible by the API key. Omit to return data from every workspace in scope. Accepts either a comma-separated string (`?workspaceIds=uuid1,uuid2`) — the documented format — or repeated array params (`?workspaceIds[]=uuid1&workspaceIds[]=uuid2`).","schema":{"example":"550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-indexed)","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Paginated list of users","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedUsersDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"One or more requested workspaceIds are outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"List organization users","tags":["Users"]}},"/v1/sequences":{"post":{"description":"Creates a new sequence. Use the /users endpoint to get organizationMembershipIds for assignedToIds and creatorId.","operationId":"SequenceController_create","parameters":[{"name":"x-workspace-id","in":"header","description":"Target workspace id (from `GET /v1/workspaces`). Required for multi-workspace API keys to designate which workspace to write to; omit for single-workspace keys, where it is resolved automatically. Must be within the API key scope.","required":false,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSequenceDto"}}}},"responses":{"201":{"description":"Sequence created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SequenceDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"Create a sequence","tags":["Sequences"]},"get":{"description":"Returns a paginated list of sequences scoped to the workspaces accessible by the API key. Use the `workspaceIds` query parameter to narrow results to a subset of those workspaces.","operationId":"SequenceController_list","parameters":[{"name":"workspaceIds","required":false,"in":"query","description":"Workspace IDs to narrow down results. Must be a subset of the workspaces accessible by the API key. Omit to return data from every workspace in scope. Accepts either a comma-separated string (`?workspaceIds=uuid1,uuid2`) — the documented format — or repeated array params (`?workspaceIds[]=uuid1&workspaceIds[]=uuid2`).","schema":{"example":"550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-indexed)","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Paginated list of sequences","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSequencesDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"One or more requested workspaceIds are outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"List sequences","tags":["Sequences"]}},"/v1/sequences/{id}":{"get":{"description":"Returns the details of a specific sequence. Returns 404 if the sequence is in a workspace outside the API key scope.","operationId":"SequenceController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Sequence details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SequenceDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Sequence not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get a sequence by ID","tags":["Sequences"]}},"/v1/sequences/{id}/contacts":{"get":{"description":"Returns a paginated list of contacts in a sequence, enriched with their tags and call history within the sequence. Supports filtering by tag label and contact status. Returns 404 if the parent sequence is in a workspace outside the API key scope.","operationId":"SequenceController_listContacts","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-indexed)","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"tag","required":false,"in":"query","description":"Filter contacts by tag label","schema":{"example":"hot-lead","type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter contacts by call status","schema":{"example":"CONNECTED","type":"string","enum":["CONNECTED","RESPONDER","ERROR","CANCELLED","RUNNING","MISSING_NUMBER"]}}],"responses":{"200":{"description":"Paginated list of sequence contacts with tags and calls","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSequenceContactsDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Sequence not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"List contacts of a sequence","tags":["Sequences"]},"post":{"description":"Adds contacts to an existing sequence. Each item may optionally specify an `assignedToWorkspaceMembershipId` (a workspace membership id obtained via `GET /v1/workspaces/:id/members`). Items without `assignedToWorkspaceMembershipId` are auto-assigned via round-robin among the sequence assignees (least loaded first). Idempotent: duplicates, contacts already in the sequence, and contacts outside the sequence scope are silently ignored. Soft-deleted sequence-contacts are resurrected (their original assignee is preserved; an `assignedToWorkspaceMembershipId` in the body is ignored for resurrected rows). Returns 404 if the sequence is outside the API key scope. On concurrent requests for the same contact, the winner among competing `assignedToWorkspaceMembershipId` values is undefined; verify with GET /v1/sequences/:id/contacts.","operationId":"SequenceController_addContacts","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactAssignmentBatchDto"}}}},"responses":{"201":{"description":"Contacts added","content":{"application/json":{"schema":{"example":{"added":3}}}}},"400":{"description":"Validation error, or an explicit `assignedToWorkspaceMembershipId` is not in the sequence assignees / not in the sequence workspace."},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Sequence not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Add contacts to a sequence","tags":["Sequences"]},"delete":{"description":"Removes contacts from an existing sequence (soft-delete). Idempotent: contacts not in the sequence are silently ignored. Returns 404 if the sequence is outside the API key scope.","operationId":"SequenceController_removeContacts","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactsBatchDto"}}}},"responses":{"200":{"description":"Contacts removed","content":{"application/json":{"schema":{"example":{"removed":2}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Sequence not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Remove contacts from a sequence","tags":["Sequences"]}},"/v1/stats/calls":{"get":{"description":"Returns call count, average duration, and total duration. Can be filtered by sequence, user, date range, and workspace. Pass `workspaceId` to narrow to a single workspace within the API key scope. The legacy `teamId` parameter is still accepted but adds a `Deprecation: true` response header.","operationId":"StatsController_getCallsDuration","parameters":[{"name":"sequenceId","required":false,"in":"query","description":"Filter by sequence ID","schema":{"type":"string"}},{"name":"userMembershipId","required":false,"in":"query","description":"Filter by user membership ID","schema":{"type":"string"}},{"name":"callType","required":false,"in":"query","description":"Filter by call type. \"all\" includes sequence + direct calls.","schema":{"default":"all","type":"string","enum":["all","sequence","direct","inbound","outbound"]}},{"name":"granularity","required":false,"in":"query","description":"Time granularity for grouping results","schema":{"default":"none","type":"string","enum":["hour","day","week","month","none"]}},{"name":"startDate","required":false,"in":"query","description":"Start date (ISO format)","schema":{"type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date (ISO format)","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Filter stats by a single workspace ID (must be within the API key scope). Stats endpoints accept exactly one workspace, unlike list endpoints which support a comma-separated `workspaceIds` filter. Replaces the deprecated `teamId`.","schema":{"type":"string"}},{"name":"teamId","required":false,"in":"query","deprecated":true,"description":"**Deprecated** — use `workspaceId` instead. When set, the response includes a `Deprecation: true` header.","schema":{"type":"string"}}],"responses":{"200":{"description":"Call duration statistics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallDurationStatsResponseDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Requested workspaceId/teamId is outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get call duration statistics","tags":["Statistics"]}},"/v1/stats/status-repartition":{"get":{"description":"Returns the distribution of calls by status, plus count of contacts not yet called. Pass `workspaceId` to narrow to a single workspace within the API key scope. The legacy `teamId` parameter is still accepted but adds a `Deprecation: true` response header.","operationId":"StatsController_getStatusRepartition","parameters":[{"name":"sequenceId","required":false,"in":"query","description":"Filter by sequence ID","schema":{"type":"string"}},{"name":"userMembershipId","required":false,"in":"query","description":"Filter by user membership ID","schema":{"type":"string"}},{"name":"callType","required":false,"in":"query","description":"Filter by call type. \"all\" includes sequence + direct calls.","schema":{"default":"all","type":"string","enum":["all","sequence","direct","inbound","outbound"]}},{"name":"granularity","required":false,"in":"query","description":"Time granularity for grouping results","schema":{"default":"none","type":"string","enum":["hour","day","week","month","none"]}},{"name":"startDate","required":false,"in":"query","description":"Start date (ISO format)","schema":{"type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date (ISO format)","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Filter stats by a single workspace ID (must be within the API key scope). Stats endpoints accept exactly one workspace, unlike list endpoints which support a comma-separated `workspaceIds` filter. Replaces the deprecated `teamId`.","schema":{"type":"string"}},{"name":"teamId","required":false,"in":"query","deprecated":true,"description":"**Deprecated** — use `workspaceId` instead. When set, the response includes a `Deprecation: true` header.","schema":{"type":"string"}}],"responses":{"200":{"description":"Status distribution statistics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusRepartitionResponseDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Requested workspaceId/teamId is outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get call status distribution","tags":["Statistics"]}},"/v1/stats/tag-repartition":{"get":{"description":"Returns the distribution of contacts by tags applied after calls. Pass `workspaceId` to narrow to a single workspace within the API key scope. The legacy `teamId` parameter is still accepted but adds a `Deprecation: true` response header.","operationId":"StatsController_getTagRepartition","parameters":[{"name":"sequenceId","required":false,"in":"query","description":"Filter by sequence ID","schema":{"type":"string"}},{"name":"userMembershipId","required":false,"in":"query","description":"Filter by user membership ID","schema":{"type":"string"}},{"name":"callType","required":false,"in":"query","description":"Filter by call type. \"all\" includes sequence + direct calls.","schema":{"default":"all","type":"string","enum":["all","sequence","direct","inbound","outbound"]}},{"name":"granularity","required":false,"in":"query","description":"Time granularity for grouping results","schema":{"default":"none","type":"string","enum":["hour","day","week","month","none"]}},{"name":"startDate","required":false,"in":"query","description":"Start date (ISO format)","schema":{"type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date (ISO format)","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Filter stats by a single workspace ID (must be within the API key scope). Stats endpoints accept exactly one workspace, unlike list endpoints which support a comma-separated `workspaceIds` filter. Replaces the deprecated `teamId`.","schema":{"type":"string"}},{"name":"teamId","required":false,"in":"query","deprecated":true,"description":"**Deprecated** — use `workspaceId` instead. When set, the response includes a `Deprecation: true` header.","schema":{"type":"string"}}],"responses":{"200":{"description":"Tag distribution statistics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRepartitionResponseDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Requested workspaceId/teamId is outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get tag distribution","tags":["Statistics"]}},"/v1/teams":{"get":{"deprecated":true,"description":"**Deprecated** — Use GET /workspaces instead.","operationId":"TeamController_listTeams","parameters":[],"responses":{"200":{"description":"List of accessible teams","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTeamListResponseDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"List teams accessible by this API key","tags":["Teams (deprecated — use /workspaces)"]}},"/v1/teams/stats":{"get":{"deprecated":true,"description":"**Deprecated** — Use GET /workspaces/stats instead.","operationId":"TeamController_getTeamStats","parameters":[{"name":"period","required":true,"in":"query","schema":{"example":"week","type":"string","enum":["day","week","month","custom"]}},{"name":"startDate","required":false,"in":"query","schema":{"example":"2026-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"example":"2026-01-31","type":"string"}},{"name":"teamId","required":false,"in":"query","description":"Filter by team ID (must be within API key scope)","schema":{"type":"string"}}],"responses":{"200":{"description":"Team stats response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTeamStatsResponseDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"Get stats aggregated by team","tags":["Teams (deprecated — use /workspaces)"]}},"/v1/workspaces":{"get":{"description":"Returns all workspaces within the scope of the API key.","operationId":"WorkspaceController_listWorkspaces","parameters":[],"responses":{"200":{"description":"List of accessible workspaces","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicWorkspaceListResponseDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"List workspaces accessible by this API key","tags":["Workspaces"]}},"/v1/workspaces/stats":{"get":{"description":"Returns call, contact, and sequence stats per workspace. Scope is limited to the API key's accessible workspaces.","operationId":"WorkspaceController_getWorkspaceStats","parameters":[{"name":"period","required":true,"in":"query","schema":{"example":"week","type":"string","enum":["day","week","month","custom"]}},{"name":"startDate","required":false,"in":"query","schema":{"example":"2026-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"example":"2026-01-31","type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Filter by workspace ID (must be within API key scope)","schema":{"type":"string"}}],"responses":{"200":{"description":"Workspace stats response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicWorkspaceStatsResponseDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"Get stats aggregated by workspace","tags":["Workspaces"]}},"/v1/workspaces/{id}/members":{"get":{"description":"Returns paginated members of a workspace. Each item exposes a workspace membership id (`id` field) usable as `assignedToWorkspaceMembershipId` when adding contacts to a sequence in this workspace. Includes members of every status; filter to ACTIVE if needed. Excludes pending workspace invitations that have no organization membership yet.","operationId":"WorkspaceController_listMembers","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"example":50,"type":"number"}}],"responses":{"200":{"description":"Paginated workspace members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedWorkspaceMembersDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Workspace not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"List members of a workspace","tags":["Workspaces"]}},"/v1/tags":{"get":{"description":"Returns a paginated list of tags scoped to the workspaces accessible by the API key. Use the `workspaceIds` query parameter to narrow results to a subset of those workspaces.","operationId":"TagController_list","parameters":[{"name":"workspaceIds","required":false,"in":"query","description":"Workspace IDs to narrow down results. Must be a subset of the workspaces accessible by the API key. Omit to return data from every workspace in scope. Accepts either a comma-separated string (`?workspaceIds=uuid1,uuid2`) — the documented format — or repeated array params (`?workspaceIds[]=uuid1&workspaceIds[]=uuid2`).","schema":{"example":"550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Paginated list of tags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedTagsDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"One or more requested workspaceIds are outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"List tags","tags":["Tags"]},"post":{"description":"Creates a new tag for use in sequences to qualify call results.","operationId":"TagController_create","parameters":[{"name":"x-workspace-id","in":"header","description":"Target workspace id (from `GET /v1/workspaces`). Required for multi-workspace API keys to designate which workspace to write to; omit for single-workspace keys, where it is resolved automatically. Must be within the API key scope.","required":false,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCreateTagDto"}}}},"responses":{"201":{"description":"Tag created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTagDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}}},"security":[{"ApiKey":[]}],"summary":"Create a tag","tags":["Tags"]}},"/v1/tags/{id}":{"get":{"description":"Returns the details of a specific tag. Returns 404 if the tag is in a workspace outside the API key scope.","operationId":"TagController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Tag details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTagDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Tag not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get a tag by ID","tags":["Tags"]},"patch":{"description":"Updates an existing tag. Only provided fields will be updated. Returns 404 if the tag is in a workspace outside the API key scope.","operationId":"TagController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicUpdateTagDto"}}}},"responses":{"200":{"description":"Tag updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTagDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Tag not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Update a tag","tags":["Tags"]},"delete":{"description":"Deletes a tag. Cannot delete the last success tag — at least one must remain per workspace. Returns 404 if the tag is in a workspace outside the API key scope.","operationId":"TagController_delete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Tag deleted successfully"},"400":{"description":"Cannot delete the last success tag"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Tag not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Delete a tag","tags":["Tags"]}},"/v1/custom-fields":{"get":{"description":"Returns a paginated list of custom fields scoped to the workspaces accessible by the API key. Each custom field has a stable `slug` (immutable) and a human-readable `name` (editable). Use the `slug` as the key when setting custom field values on contacts. Use the `workspaceIds` query parameter to narrow results to a subset of those workspaces.","operationId":"CustomFieldController_list","parameters":[{"name":"workspaceIds","required":false,"in":"query","description":"Workspace IDs to narrow down results. Must be a subset of the workspaces accessible by the API key. Omit to return data from every workspace in scope. Accepts either a comma-separated string (`?workspaceIds=uuid1,uuid2`) — the documented format — or repeated array params (`?workspaceIds[]=uuid1&workspaceIds[]=uuid2`).","schema":{"example":"550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-indexed)","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":100,"default":10,"example":10,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search query to filter custom fields by name","schema":{"maxLength":100,"example":"job","type":"string"}}],"responses":{"200":{"description":"Paginated list of custom fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedPublicCustomFieldsDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"One or more requested workspaceIds are outside the API key scope"}},"security":[{"ApiKey":[]}],"summary":"List custom fields","tags":["Custom Fields"]},"post":{"description":"Creates a new custom field in your organization. A stable `slug` is automatically generated from the `name` and returned in the response. The slug is immutable: renaming the custom field later will not change it.","operationId":"CustomFieldController_create","parameters":[{"name":"x-workspace-id","in":"header","description":"Target workspace id (from `GET /v1/workspaces`). Required for multi-workspace API keys to designate which workspace to write to; omit for single-workspace keys, where it is resolved automatically. Must be within the API key scope.","required":false,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCreateCustomFieldDto"}}}},"responses":{"201":{"description":"The created custom field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCustomFieldDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"409":{"description":"A custom field with the same name already exists"}},"security":[{"ApiKey":[]}],"summary":"Create a custom field","tags":["Custom Fields"]}},"/v1/custom-fields/{id}":{"get":{"description":"Returns a single custom field by ID. Returns 404 if the custom field is in a workspace outside the API key scope.","operationId":"CustomFieldController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The custom field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCustomFieldDto"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Custom field not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Get a custom field","tags":["Custom Fields"]},"patch":{"description":"Updates an existing custom field name. The `slug` is **not** modified by this operation — it remains stable for integrations. Returns 404 if the custom field is in a workspace outside the API key scope.","operationId":"CustomFieldController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicUpdateCustomFieldDto"}}}},"responses":{"200":{"description":"The updated custom field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCustomFieldDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Custom field not found or outside API key scope"},"409":{"description":"A custom field with the same name already exists"}},"security":[{"ApiKey":[]}],"summary":"Update a custom field","tags":["Custom Fields"]},"delete":{"description":"Permanently deletes a custom field and all its associated values across contacts. Returns 404 if the custom field is in a workspace outside the API key scope.","operationId":"CustomFieldController_delete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Custom field deleted successfully"},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or revoked API key"},"status":{"type":"number","example":401}}}}}}}},"403":{"description":"Subscription required or plan not eligible","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"PLAN_NOT_ELIGIBLE"},"message":{"type":"string","example":"API access requires INTERMÉDIAIRE or EXPERT plan"},"status":{"type":"number","example":403}}}}}}}},"404":{"description":"Custom field not found or outside API key scope"}},"security":[{"ApiKey":[]}],"summary":"Delete a custom field","tags":["Custom Fields"]}}},"info":{"title":"Skipcall Public API","description":"## Overview\nThe Skipcall Public API allows you to integrate Skipcall with your applications, CRM, or automation tools.\n\n## Plan Requirements\nAPI access is available for **STARTER**, **INTERMÉDIAIRE** and **EXPERT** plans.\n\n## Authentication\nAll API requests require authentication using an API Key. Include your API key in the `Authorization` header:\n\n```\nAuthorization: Bearer sk_live_your_api_key_here\n```\n\nYou can generate API keys from your Skipcall dashboard under Settings > API Keys.\n\n## Rate Limiting\nRate limits vary by subscription plan:\n\n| Plan | Rate Limit |\n|------|------------|\n| STARTER | 100 requests/minute |\n| INTERMÉDIAIRE | 100 requests/minute |\n| EXPERT | 1000 requests/minute |\n\nRate limit headers are included in every response:\n- `X-RateLimit-Limit`: Maximum requests per minute for your plan\n- `X-RateLimit-Remaining`: Remaining requests in the current window\n- `X-RateLimit-Reset`: Unix timestamp when the rate limit resets\n\n## Errors\nThe API uses standard HTTP status codes:\n- `200` - Success\n- `400` - Bad Request (invalid parameters)\n- `401` - Unauthorized (invalid or missing API key)\n- `403` - Forbidden (subscription required or plan not eligible)\n- `404` - Not Found\n- `429` - Too Many Requests (rate limit exceeded)\n- `500` - Internal Server Error\n\n### Subscription Errors\n| Code | Description |\n|------|-------------|\n| `SUBSCRIPTION_REQUIRED` | An active subscription is required to access the API |\n| `PLAN_NOT_ELIGIBLE` | API access requires STARTER, INTERMÉDIAIRE or EXPERT plan |","version":"1.0","contact":{}},"tags":[{"name":"Contacts","description":"Manage your contacts"},{"name":"Calls","description":"Access call history and recordings"}],"servers":[{"url":"https://api-public.skipcall.app","description":"Production"}],"components":{"securitySchemes":{"ApiKey":{"scheme":"bearer","bearerFormat":"API Key","type":"http","description":"Enter your API Key (sk_live_xxx or sk_test_xxx)"}},"schemas":{"PublicContactDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"Doe"},"email":{"type":"object","example":"john.doe@example.com","nullable":true},"company":{"type":"object","example":"Acme Inc.","nullable":true},"city":{"type":"object","example":"Paris","nullable":true},"country":{"type":"object","example":"France","nullable":true},"url":{"type":"object","example":"https://example.com","nullable":true},"linkedinUrl":{"type":"object","example":"https://linkedin.com/in/johndoe","nullable":true},"linkedinSalesUrl":{"type":"object","example":"https://linkedin.com/sales/people/johndoe","nullable":true},"role":{"type":"object","example":"CTO","nullable":true},"allowPhoneCalls":{"type":"boolean","example":true},"callCount":{"type":"number","example":5},"lastCallAt":{"format":"date-time","type":"string","nullable":true},"teamId":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"phoneNumbers":{"example":["+33612345678"],"type":"array","items":{"type":"string"}},"customFields":{"type":"object","example":{"Industry":"Technology","Size":"50-100"},"additionalProperties":{"type":"string"}},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"lastModificationSource":{"type":"string","enum":["SKIPCALL","API"]}},"required":["id","firstName","lastName","allowPhoneCalls","callCount","teamId","phoneNumbers","customFields","createdAt","updatedAt","lastModificationSource"]},"PaginatedPublicContactsDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicContactDto"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"PublicCreateContactDto":{"type":"object","properties":{"firstName":{"type":"string","description":"First name of the contact","example":"John","minLength":1,"maxLength":100},"lastName":{"type":"string","description":"Last name of the contact","example":"Doe","minLength":1,"maxLength":100},"email":{"type":"string","description":"Email address of the contact","example":"john.doe@example.com"},"company":{"type":"string","description":"Company name","example":"Acme Inc.","maxLength":200},"city":{"type":"string","description":"City","example":"Paris","maxLength":200},"country":{"type":"string","description":"Country","example":"France","maxLength":200},"url":{"type":"string","description":"Website URL","example":"https://example.com"},"linkedinUrl":{"type":"string","description":"LinkedIn profile URL","example":"https://linkedin.com/in/johndoe"},"linkedinSalesUrl":{"type":"string","description":"LinkedIn Sales Navigator URL","example":"https://linkedin.com/sales/people/johndoe"},"role":{"type":"string","description":"Role / job title","example":"CTO","maxLength":200},"allowPhoneCalls":{"type":"boolean","description":"Whether the contact accepts phone calls. Defaults to true.","example":true},"phoneNumbers":{"description":"Phone numbers in E.164 format (e.g., +33612345678)","example":["+33612345678"],"minItems":1,"maxItems":20,"type":"array","items":{"type":"string"}},"customFields":{"type":"object","description":"Custom field values keyed by the custom field **slug** (the stable, immutable identifier returned by GET /custom-fields). Custom fields must already exist for the team (use the Custom Fields endpoints to create them). For backwards compatibility this endpoint also accepts keys matching the custom field **name**, but this fallback is deprecated and will be removed in a future release — migrate your integrations to use slugs.","example":{"industry":"Technology","team_size":"50-100"},"additionalProperties":{"type":"string"}}},"required":["firstName","lastName","phoneNumbers"]},"PublicUpdateContactDto":{"type":"object","properties":{"firstName":{"type":"string","description":"First name of the contact","example":"John","minLength":1,"maxLength":100},"lastName":{"type":"string","description":"Last name of the contact","example":"Doe","minLength":1,"maxLength":100},"email":{"type":"string","description":"Email address of the contact","example":"john.doe@example.com"},"company":{"type":"string","description":"Company name","example":"Acme Inc.","maxLength":200},"city":{"type":"string","description":"City","example":"Paris","maxLength":200},"country":{"type":"string","description":"Country","example":"France","maxLength":200},"url":{"type":"string","description":"Website URL","example":"https://example.com"},"linkedinUrl":{"type":"string","description":"LinkedIn profile URL","example":"https://linkedin.com/in/johndoe"},"linkedinSalesUrl":{"type":"string","description":"LinkedIn Sales Navigator URL","example":"https://linkedin.com/sales/people/johndoe"},"role":{"type":"string","description":"Role / job title","example":"CTO","maxLength":200},"allowPhoneCalls":{"type":"boolean","description":"Whether the contact accepts phone calls","example":true},"phoneNumbers":{"description":"Phone numbers in E.164 format. If provided, replaces all existing phone numbers.","example":["+33612345678","+33698765432"],"minItems":1,"maxItems":20,"type":"array","items":{"type":"string"}},"customFields":{"type":"object","description":"Custom field values keyed by the custom field **slug** (the stable, immutable identifier returned by GET /custom-fields). Only provided keys are updated; omit to leave existing values untouched. Custom fields must already exist for the team. For backwards compatibility this endpoint also accepts keys matching the custom field **name**, but this fallback is deprecated and will be removed in a future release — migrate your integrations to use slugs.","example":{"industry":"Technology","team_size":"50-100"},"additionalProperties":{"type":"string"}}}},"CallContactSummaryDTO":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the contact"},"firstName":{"type":"string","example":"John","description":"First name of the contact"},"lastName":{"type":"string","example":"Doe","description":"Last name of the contact"},"isUnknown":{"type":"boolean","example":false,"description":"Whether this is an unknown contact (not in the system)"}},"required":["id","firstName","lastName","isUnknown"]},"CallPhoneNumberSummaryDTO":{"type":"object","properties":{"value":{"type":"string","example":"+33612345678","description":"The phone number value"}},"required":["value"]},"CallCallerSummaryDTO":{"type":"object","properties":{"firstName":{"type":"string","example":"Alice","description":"First name of the caller"},"lastName":{"type":"string","example":"Martin","description":"Last name of the caller"}},"required":["firstName","lastName"]},"CallWithContactDTO":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the call history entry"},"callType":{"type":"string","example":"CALL_SESSION","description":"Type of the call history entry (e.g., CALL_SESSION, DIRECT_CALL)","enum":["CALL_SESSION","DIRECT_CALL"]},"phoneNumberId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174002","description":"Unique identifier of the phone number used for the call"},"userProviderSid":{"type":"string","example":"CA123456789","description":"Provider SID for the user side of the call"},"adminProviderSid":{"type":"object","example":"CA987654321","description":"Provider SID for the admin side of the call","nullable":true},"providerConferenceId":{"type":"string","example":"CF123456789","description":"Provider conference ID"},"status":{"type":"string","example":"completed","description":"Status of the call"},"type":{"type":"string","example":"outbound","description":"Type of call (e.g., outbound, inbound)"},"transferred":{"type":"boolean","example":false,"description":"Indicates if the call was transferred"},"startedAt":{"type":"object","example":"2024-03-20T09:00:00.000Z","description":"Start time of the call","nullable":true},"endedAt":{"type":"object","example":"2024-03-20T09:15:00.000Z","description":"End time of the call","nullable":true},"createdAt":{"format":"date-time","type":"string","example":"2024-03-20T08:55:00.000Z","description":"Date and time when the call history entry was created"},"updatedAt":{"format":"date-time","type":"string","example":"2024-03-20T09:16:00.000Z","description":"Date and time when the call history entry was last updated"},"sequenceRunId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174003","description":"Unique identifier of the related sequence run"},"sequenceId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174010","description":"Unique identifier of the parent sequence"},"syncedWithCrmDate":{"type":"object","example":"2024-03-20T09:15:00.000Z","description":"Date and time when the call was synced with CRM","nullable":true},"deletedAt":{"format":"date-time","type":"string","example":"2024-03-20T09:15:00.000Z","description":"Date and time when the call history entry was deleted","nullable":true},"organizationId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174004","description":"Unique identifier of the organization"},"organizationMembershipId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174005","description":"Unique identifier of the organization membership"},"managedPhoneNumberId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174006","description":"Unique identifier of the managed phone number"},"notes":{"type":"string","example":"Call notes here","description":"Notes associated with the call"},"contact":{"description":"Contact information associated with the call","nullable":true,"allOf":[{"$ref":"#/components/schemas/CallContactSummaryDTO"}]},"phoneNumber":{"description":"Phone number information associated with the call","nullable":true,"allOf":[{"$ref":"#/components/schemas/CallPhoneNumberSummaryDTO"}]},"caller":{"description":"Caller information (organization member who made the call)","nullable":true,"allOf":[{"$ref":"#/components/schemas/CallCallerSummaryDTO"}]},"callerPhoneNumber":{"description":"Managed phone number used by the caller","nullable":true,"allOf":[{"$ref":"#/components/schemas/CallPhoneNumberSummaryDTO"}]}},"required":["id","callType","status","type","startedAt","endedAt","createdAt","contact","phoneNumber","caller","callerPhoneNumber"]},"PaginatedCallDTO":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CallWithContactDTO"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"PublicCallRecordingDTO":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the recording"},"duration":{"type":"number","example":245,"description":"Duration of the recording in seconds"},"startTime":{"format":"date-time","type":"string","example":"2024-01-15T10:00:00Z","description":"Start time of the recording"},"hasTranscription":{"type":"boolean","example":true,"description":"Indicates if a transcription exists for this recording"},"transcriptionStatus":{"type":"string","example":"COMPLETED","description":"Status of the transcription if it exists","enum":["PENDING","IN_PROGRESS","FAILED","COMPLETED"],"nullable":true}},"required":["id","duration","startTime","hasTranscription","transcriptionStatus"]},"TranscriptUtteranceDTO":{"type":"object","properties":{"speaker":{"type":"number","example":0,"description":"Speaker identifier (0 or 1)"},"text":{"type":"string","example":"Hello, this is John from...","description":"Transcribed text for this utterance"},"start":{"type":"number","example":0.5,"description":"Start time in seconds"},"end":{"type":"number","example":3.2,"description":"End time in seconds"},"confidence":{"type":"number","example":0.95,"description":"Confidence score (0-1)"}},"required":["speaker","text","start","end","confidence"]},"PublicCallTranscriptionDTO":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the transcription"},"status":{"type":"string","example":"COMPLETED","description":"Status of the transcription","enum":["PENDING","IN_PROGRESS","FAILED","COMPLETED"]},"transcript":{"description":"Array of transcribed utterances","nullable":true,"type":"array","items":{"$ref":"#/components/schemas/TranscriptUtteranceDTO"}},"summary":{"type":"string","example":"Discussion about product features...","description":"AI-generated summary of the conversation","nullable":true},"startedAt":{"type":"string","format":"date-time","example":"2024-01-15T10:00:00Z","description":"When transcription processing started","nullable":true},"endedAt":{"type":"string","format":"date-time","example":"2024-01-15T10:00:45Z","description":"When transcription processing completed","nullable":true}},"required":["id","status","transcript","summary","startedAt","endedAt"]},"UserDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the user"},"organizationMembershipId":{"type":"string","example":"987e6543-e21b-45d3-b678-426614174001","description":"Unique identifier of the organization membership. Use this ID when creating sequences."},"firstName":{"type":"object","example":"John","description":"First name of the user","nullable":true},"lastName":{"type":"object","example":"Doe","description":"Last name of the user","nullable":true},"email":{"type":"string","example":"john.doe@example.com","description":"Email address of the user"},"role":{"type":"string","example":"MEMBER","description":"Role of the user in the organization","enum":["ADMIN","MEMBER","MANAGER"]},"status":{"type":"string","example":"ACTIVE","description":"Status of the user membership in the organization","enum":["ACTIVE","INACTIVE","PENDING"]}},"required":["id","organizationMembershipId","email","role","status"]},"PaginatedUsersDto":{"type":"object","properties":{"data":{"description":"Array of users","type":"array","items":{"$ref":"#/components/schemas/UserDto"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"CreateSequenceDto":{"type":"object","properties":{"name":{"type":"string","description":"Sequence name","example":"My Sales Sequence"},"assignedToIds":{"description":"OrganizationMembership IDs of users to assign this sequence to","example":["123e4567-e89b-12d3-a456-426614174001","123e4567-e89b-12d3-a456-426614174002"],"type":"array","items":{"type":"string"}},"creatorId":{"type":"string","description":"OrganizationMembership ID of the creator. Use the /users endpoint to get available organizationMembershipIds.","example":"123e4567-e89b-12d3-a456-426614174000"},"contactIds":{"description":"Contact IDs to add to the sequence","example":["123e4567-e89b-12d3-a456-426614174010","123e4567-e89b-12d3-a456-426614174011"],"type":"array","items":{"type":"string"}}},"required":["name","assignedToIds","creatorId"]},"AssignedUserDto":{"type":"object","properties":{"organizationMembershipId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"OrganizationMembership ID of the assigned user"},"firstName":{"type":"object","example":"John","description":"First name of the assigned user","nullable":true},"lastName":{"type":"object","example":"Doe","description":"Last name of the assigned user","nullable":true},"email":{"type":"string","example":"john.doe@example.com","description":"Email of the assigned user"}},"required":["organizationMembershipId","email"]},"SequenceDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the sequence"},"name":{"type":"string","example":"My Sales Sequence","description":"Name of the sequence"},"organizationMembershipId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174001","description":"OrganizationMembership ID of the creator"},"createdAt":{"format":"date-time","type":"string","example":"2024-01-15T10:30:00.000Z","description":"Date when the sequence was created"},"updatedAt":{"format":"date-time","type":"string","example":"2024-01-15T10:30:00.000Z","description":"Date when the sequence was last updated"},"contactCount":{"type":"number","example":150,"description":"Number of contacts in the sequence"},"assignedTo":{"description":"List of users assigned to this sequence","type":"array","items":{"$ref":"#/components/schemas/AssignedUserDto"}}},"required":["id","name","organizationMembershipId","createdAt","updatedAt"]},"PaginatedSequencesDto":{"type":"object","properties":{"data":{"description":"Array of sequences","type":"array","items":{"$ref":"#/components/schemas/SequenceDto"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"SequenceContactPhoneNumberDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the phone number"},"number":{"type":"string","example":"+33612345678","description":"Phone number in E.164 format"}},"required":["id","number"]},"SequenceContactContactDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the contact"},"firstName":{"type":"string","example":"John","description":"First name of the contact"},"lastName":{"type":"string","example":"Doe","description":"Last name of the contact"},"email":{"type":"object","example":"john.doe@example.com","description":"Email address of the contact","nullable":true},"company":{"type":"object","example":"Acme Corp","description":"Company of the contact","nullable":true},"phoneNumbers":{"description":"Phone numbers associated with the contact","type":"array","items":{"$ref":"#/components/schemas/SequenceContactPhoneNumberDto"}}},"required":["id","firstName","lastName"]},"SequenceContactTagDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the tag"},"label":{"type":"string","example":"hot-lead","description":"Label of the tag"},"isSuccessTag":{"type":"boolean","example":true,"description":"Whether this tag is a success tag"}},"required":["id","label","isSuccessTag"]},"SequenceContactCallDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the call session"},"status":{"type":"string","enum":["NOT_INITIATED","INITIATED","QUEUED","RINGING","IN_PROGRESS","BUSY","NO_ANSWER","MISSED_CALL","VOICEMAIL","CONNECTED","CANCELLED","COMPLETED","ERROR_NUMBER","ERROR_COUNTRY","UNKNOWN_ERROR"],"example":"COMPLETED","description":"Status of the call"},"startedAt":{"type":"object","example":"2024-01-15T10:30:00.000Z","description":"Date and time when the call started","nullable":true},"endedAt":{"type":"object","example":"2024-01-15T10:32:30.000Z","description":"Date and time when the call ended","nullable":true},"durationSeconds":{"type":"object","example":150,"description":"Duration of the call in seconds (endedAt - startedAt)","nullable":true},"createdAt":{"format":"date-time","type":"string","example":"2024-01-15T10:30:00.000Z","description":"Date and time when the call record was created"}},"required":["id","status","createdAt"]},"SequenceContactDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the sequence contact record"},"status":{"type":"string","enum":["CONNECTED","RESPONDER","ERROR","CANCELLED","RUNNING","MISSING_NUMBER"],"example":"CONNECTED","description":"Status of the contact in the sequence","nullable":true},"contact":{"description":"Contact information","allOf":[{"$ref":"#/components/schemas/SequenceContactContactDto"}]},"tags":{"description":"Tags associated with this contact in the sequence","type":"array","items":{"$ref":"#/components/schemas/SequenceContactTagDto"}},"calls":{"description":"Calls made to this contact in the sequence","type":"array","items":{"$ref":"#/components/schemas/SequenceContactCallDto"}},"createdAt":{"format":"date-time","type":"string","example":"2024-01-15T10:30:00.000Z","description":"Date when the contact was added to the sequence"}},"required":["id","contact","tags","calls","createdAt"]},"PaginatedSequenceContactsDto":{"type":"object","properties":{"data":{"description":"Array of sequence contacts","type":"array","items":{"$ref":"#/components/schemas/SequenceContactDto"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"ContactAssignmentDto":{"type":"object","properties":{"contactId":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000","description":"The contact UUID (v4)."},"assignedToWorkspaceMembershipId":{"type":"string","format":"uuid","example":"987e6543-e21b-45d3-b678-426614174001","description":"Optional workspace-membership UUID identifying the assignee (the user within the workspace). Obtain via GET /v1/workspaces/:id/members. If omitted, the contact is auto-assigned via round-robin among the sequence assignees. If provided, must belong to the sequence workspace AND be one of the sequence assignees, otherwise 400."}},"required":["contactId"]},"ContactAssignmentBatchDto":{"type":"object","properties":{"contacts":{"minItems":1,"maxItems":100,"description":"Array of 1–100 contact assignment entries. Per item: `contactId` is required; `assignedToWorkspaceMembershipId` optional. Duplicates, contacts already in the sequence, and out-of-scope contacts are silently ignored.","type":"array","items":{"$ref":"#/components/schemas/ContactAssignmentDto"}}},"required":["contacts"]},"ContactsBatchDto":{"type":"object","properties":{"contactIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":100,"example":["123e4567-e89b-12d3-a456-426614174000","123e4567-e89b-12d3-a456-426614174001"],"description":"Array of contact UUIDs (1–100). Duplicates and out-of-scope contacts are ignored silently."}},"required":["contactIds"]},"CallDurationStatsDto":{"type":"object","properties":{"callCount":{"type":"number","description":"Number of calls"},"avgDurationInSeconds":{"type":"number","description":"Average call duration in seconds"},"totalDurationInSeconds":{"type":"number","description":"Total call duration in seconds"},"date":{"type":"string","description":"Date bucket (when granularity is not \"none\")","example":"2023-10-01T00:00:00.000Z"}},"required":["callCount","avgDurationInSeconds","totalDurationInSeconds"]},"CallDurationStatsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CallDurationStatsDto"}}},"required":["data"]},"CallSessionStatuses":{"type":"string","enum":["NOT_INITIATED","INITIATED","QUEUED","RINGING","IN_PROGRESS","BUSY","NO_ANSWER","MISSED_CALL","VOICEMAIL","CONNECTED","CANCELLED","COMPLETED","ERROR_NUMBER","ERROR_COUNTRY","UNKNOWN_ERROR"],"description":"Call session status"},"StatusRepartitionItemDto":{"type":"object","properties":{"status":{"description":"Call session status","allOf":[{"$ref":"#/components/schemas/CallSessionStatuses"}]},"count":{"type":"number","description":"Number of calls with this status"},"date":{"type":"string","description":"Date bucket (when granularity is not \"none\")","example":"2023-10-01T00:00:00.000Z"}},"required":["status","count"]},"StatusRepartitionResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StatusRepartitionItemDto"}},"nonCalledCount":{"type":"number","description":"Number of contacts that have not been called yet"}},"required":["data","nonCalledCount"]},"TagRepartitionItemDto":{"type":"object","properties":{"tagId":{"type":"string","description":"Tag ID"},"label":{"type":"string","description":"Tag label"},"count":{"type":"number","description":"Number of contacts with this tag"},"isSuccess":{"type":"boolean","description":"Whether this tag indicates success"},"date":{"type":"string","description":"Date bucket (when granularity is not \"none\")","example":"2023-10-01T00:00:00.000Z"}},"required":["tagId","label","count","isSuccess"]},"TagRepartitionResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TagRepartitionItemDto"}}},"required":["data"]},"PublicTeamDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","example":"Sales"},"isDefault":{"type":"boolean","example":false},"memberCount":{"type":"number","example":5}},"required":["id","name","isDefault","memberCount"]},"PublicTeamListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicTeamDto"}}},"required":["data"]},"PublicAggregateStatsDto":{"type":"object","properties":{"totalCalls":{"type":"number","example":1250},"totalCallDuration":{"type":"number","example":45000},"totalContacts":{"type":"number","example":3400},"activeSequences":{"type":"number","example":12}},"required":["totalCalls","totalCallDuration","totalContacts","activeSequences"]},"PublicTeamStatsItemDto":{"type":"object","properties":{"teamId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"teamName":{"type":"string","example":"Sales"},"calls":{"type":"number","example":450},"callDuration":{"type":"number","example":15000},"contacts":{"type":"number","example":1200},"activeSequences":{"type":"number","example":4},"members":{"type":"number","example":5}},"required":["teamId","teamName","calls","callDuration","contacts","activeSequences","members"]},"PublicTeamStatsResponseDto":{"type":"object","properties":{"aggregate":{"$ref":"#/components/schemas/PublicAggregateStatsDto"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/PublicTeamStatsItemDto"}}},"required":["aggregate","teams"]},"PublicWorkspaceDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","example":"Sales"},"isDefault":{"type":"boolean","example":false},"memberCount":{"type":"number","example":5}},"required":["id","name","isDefault","memberCount"]},"PublicWorkspaceListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicWorkspaceDto"}}},"required":["data"]},"PublicWorkspaceAggregateStatsDto":{"type":"object","properties":{"totalCalls":{"type":"number","example":1250},"totalCallDuration":{"type":"number","example":45000},"totalContacts":{"type":"number","example":3400},"activeSequences":{"type":"number","example":12}},"required":["totalCalls","totalCallDuration","totalContacts","activeSequences"]},"PublicWorkspaceStatsItemDto":{"type":"object","properties":{"workspaceId":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000"},"workspaceName":{"type":"string","example":"Sales"},"calls":{"type":"number","example":450},"callDuration":{"type":"number","example":15000},"contacts":{"type":"number","example":1200},"activeSequences":{"type":"number","example":4},"members":{"type":"number","example":5}},"required":["workspaceId","workspaceName","calls","callDuration","contacts","activeSequences","members"]},"PublicWorkspaceStatsResponseDto":{"type":"object","properties":{"aggregate":{"$ref":"#/components/schemas/PublicWorkspaceAggregateStatsDto"},"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/PublicWorkspaceStatsItemDto"}}},"required":["aggregate","workspaces"]},"WorkspaceMemberUserDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000"},"firstName":{"type":"object","nullable":true,"example":"Alice"},"lastName":{"type":"object","nullable":true,"example":"Dupont"},"email":{"type":"string","example":"alice@example.com"}},"required":["id","email"]},"WorkspaceMemberDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Workspace membership ID (the user within the workspace). Use this as `assignedToWorkspaceMembershipId` when adding contacts to a sequence in this workspace.","example":"987e6543-e21b-45d3-b678-426614174001"},"role":{"type":"string","enum":["ADMIN","MEMBER","MANAGER"],"example":"MEMBER","description":"Organization-level role of the user. Not the workspace-scoped role."},"status":{"type":"string","enum":["ACTIVE","INACTIVE","PENDING"],"example":"ACTIVE","description":"Organization-level membership status. Not the workspace-scoped status."},"user":{"$ref":"#/components/schemas/WorkspaceMemberUserDto"}},"required":["id","role","status","user"]},"PaginatedWorkspaceMembersDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkspaceMemberDto"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"PublicTagDto":{"type":"object","properties":{"id":{"type":"string","example":"123e4567-e89b-12d3-a456-426614174000","description":"Unique identifier of the tag"},"label":{"type":"string","example":"Lead","description":"Label of the tag"},"isSuccessTag":{"type":"boolean","example":true,"description":"Whether the tag represents a successful call outcome"},"createdAt":{"format":"date-time","type":"string","example":"2024-01-15T10:30:00.000Z","description":"Date when the tag was created"},"updatedAt":{"format":"date-time","type":"string","example":"2024-01-15T10:30:00.000Z","description":"Date when the tag was last updated"}},"required":["id","label","isSuccessTag","createdAt","updatedAt"]},"PaginatedTagsDto":{"type":"object","properties":{"data":{"description":"Array of tags","type":"array","items":{"$ref":"#/components/schemas/PublicTagDto"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"PublicCreateTagDto":{"type":"object","properties":{"label":{"type":"string","example":"Lead","description":"The label of the tag"},"isSuccessTag":{"type":"boolean","example":false,"description":"Whether the tag represents a successful call outcome","default":false}},"required":["label"]},"PublicUpdateTagDto":{"type":"object","properties":{"label":{"type":"string","example":"Lead","description":"The label of the tag"},"isSuccessTag":{"type":"boolean","example":false,"description":"Whether the tag represents a successful call outcome","default":false}}},"PublicCustomFieldDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","example":"Job Title","description":"Human-readable label. Can be renamed without breaking integrations."},"slug":{"type":"string","example":"job_title","description":"Stable, immutable identifier derived from the name at creation time. Use this as the key when setting custom field values on a contact via POST/PATCH /contacts."},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","name","slug","createdAt","updatedAt"]},"PaginatedPublicCustomFieldsDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicCustomFieldDto"}},"total":{"type":"number","example":100},"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"totalPages":{"type":"number","example":10}},"required":["data","total","page","limit","totalPages"]},"PublicCreateCustomFieldDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the custom field","example":"Job Title","minLength":1,"maxLength":200}},"required":["name"]},"PublicUpdateCustomFieldDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the custom field","example":"Job Title","minLength":1,"maxLength":200}},"required":["name"]}}}}