{"openapi":"3.0.3","info":{"title":"DeDupe API","description":"API for managing duplicate detection and merge operations in Attio. This API enables you to trigger duplicate scans, query detected duplicates, and execute merges programmatically.\n\n## Authentication\n\nAll API endpoints require authentication using an API key. API keys can be generated from the dashboard settings.\n\n### Supported Authentication Methods\n\n1. **Bearer Token** (Recommended): Include your API key in the `Authorization` header:\n   ```\n   Authorization: Bearer attio_pk_your_api_key_here\n   ```\n\n2. **X-API-Key Header**: Alternatively, use the `X-API-Key` header:\n   ```\n   X-API-Key: attio_pk_your_api_key_here\n   ```\n\n### API Key Scopes\n\nAPI keys can be created with specific scopes to limit access:\n\n| Scope | Description |\n|-------|-------------|\n| `scans:trigger` | Trigger duplicate detection scans |\n| `duplicates:read` | Query and view duplicate pairs |\n| `merges:execute` | Approve and execute merge operations |\n\n## Rate Limiting\n\nAPI requests are rate limited to ensure fair usage. If you exceed the rate limit, you'll receive a `429 Too Many Requests` response.\n\n## Error Handling\n\nAll errors follow a consistent format:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ERROR_CODE\",\n    \"message\": \"Human-readable error message\",\n    \"details\": {}\n  }\n}\n```\n\n### Common Error Codes\n\n| Code | Description |\n|------|-------------|\n| `API_KEY_REQUIRED` | No API key provided |\n| `INVALID_API_KEY` | API key is invalid or expired |\n| `INSUFFICIENT_SCOPE` | API key lacks required scope |\n| `VALIDATION_ERROR` | Request body validation failed |\n| `NOT_FOUND` | Resource not found |\n| `CONFLICT` | Operation conflicts with current state |","version":"1.0.0","contact":{"name":"API Support","email":"support@usededupe.com"}},"servers":[{"url":"/api/v1","description":"Current Server"}],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"tags":[{"name":"Scans","description":"Duplicate detection scan operations"},{"name":"Duplicates","description":"Query and manage detected duplicate pairs"},{"name":"Merges","description":"Execute and manage merge operations"},{"name":"Health","description":"API health and status"}],"paths":{"/scans":{"post":{"tags":["Scans"],"summary":"Trigger Duplicate Scan","description":"Triggers a duplicate detection scan for the specified workspace. The scan runs asynchronously and you'll receive a run ID to track progress.\n\n**Required Scope:** `scans:trigger`","operationId":"triggerScan","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerScanRequest"},"examples":{"basic":{"summary":"Basic scan","description":"Trigger a basic scan for companies","value":{"workspaceId":"550e8400-e29b-41d4-a716-446655440000"}},"advanced":{"summary":"Advanced scan with options","description":"Trigger a scan with specific matchers and confidence threshold","value":{"workspaceId":"550e8400-e29b-41d4-a716-446655440000","objectType":"companies","matcherTypes":["similar_domain","similar_email"],"minConfidenceScore":0.8,"maxResults":100,"skipExisting":true,"storeDuplicates":true}}}}}},"responses":{"202":{"description":"Scan initiated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerScanResponse"},"example":{"success":true,"data":{"runId":"run_01HQJXK5M3K2NDQJ5HWK8YT5VR","workspaceId":"550e8400-e29b-41d4-a716-446655440000","objectType":"companies","message":"Duplicate scan initiated successfully"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_WORKSPACE_ID","message":"Invalid workspace ID format"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"API_KEY_REQUIRED","message":"API key required"}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"INSUFFICIENT_SCOPE","message":"API key requires scope(s): scans:trigger"}}}}},"404":{"description":"Workspace not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"WORKSPACE_NOT_FOUND","message":"Workspace not found: 550e8400-e29b-41d4-a716-446655440000"}}}}}}}},"/duplicates":{"get":{"tags":["Duplicates"],"summary":"List Duplicate Pairs","description":"Query duplicate pairs with filtering and pagination. Returns detected duplicates for the specified workspace.\n\n**Required Scope:** `duplicates:read`","operationId":"listDuplicates","parameters":[{"name":"workspaceId","in":"query","required":true,"description":"The workspace ID to query duplicates from","schema":{"type":"string","format":"uuid"},"example":"550e8400-e29b-41d4-a716-446655440000"},{"name":"status","in":"query","required":false,"description":"Filter by duplicate pair status","schema":{"type":"string","enum":["pending","confirmed","rejected","deferred","merged","auto_merged"]}},{"name":"objectType","in":"query","required":false,"description":"Filter by object type","schema":{"type":"string","enum":["people","companies"]}},{"name":"minConfidence","in":"query","required":false,"description":"Minimum confidence score (0-1)","schema":{"type":"number","minimum":0,"maximum":1},"example":0.8},{"name":"maxConfidence","in":"query","required":false,"description":"Maximum confidence score (0-1)","schema":{"type":"number","minimum":0,"maximum":1}},{"name":"page","in":"query","required":false,"description":"Page number for pagination","schema":{"type":"integer","minimum":1,"default":1}},{"name":"pageSize","in":"query","required":false,"description":"Number of items per page (max 100)","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"List of duplicate pairs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicatePairsResponse"},"example":{"success":true,"data":[{"id":"550e8400-e29b-41d4-a716-446655440001","workspaceId":"550e8400-e29b-41d4-a716-446655440000","record1Id":"550e8400-e29b-41d4-a716-446655440002","record2Id":"550e8400-e29b-41d4-a716-446655440003","objectType":"companies","confidenceScore":0.95,"matchingFields":["domain","name"],"status":"pending","detectedAt":"2024-01-15T10:30:00Z","record1":{"data":{"name":"Acme Inc","domain":"acme.com"},"attioRecordId":"rec_abc123"},"record2":{"data":{"name":"ACME Inc.","domain":"acme.com"},"attioRecordId":"rec_def456"}}],"pagination":{"page":1,"pageSize":20,"total":150,"totalPages":8}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/merges":{"post":{"tags":["Merges"],"summary":"Execute Merge","description":"Approves a duplicate pair and triggers merge execution. The merge runs asynchronously and you'll receive a run ID to track progress.\n\n**Required Scope:** `merges:execute`","operationId":"executeMerge","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteMergeRequest"},"examples":{"basic":{"summary":"Basic merge","description":"Execute a merge with default settings","value":{"duplicatePairId":"550e8400-e29b-41d4-a716-446655440001"}},"advanced":{"summary":"Merge with overrides","description":"Execute a merge with custom primary record and priority","value":{"duplicatePairId":"550e8400-e29b-41d4-a716-446655440001","primaryRecordId":"550e8400-e29b-41d4-a716-446655440002","secondaryRecordId":"550e8400-e29b-41d4-a716-446655440003","priority":"high","mergeStrategy":{"name":"keep_primary","email":"keep_secondary"}}}}}}},"responses":{"202":{"description":"Merge execution triggered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteMergeResponse"},"example":{"success":true,"data":{"message":"Merge approved and execution triggered successfully","runId":"run_01HQJXK5M3K2NDQJ5HWK8YT5VR","mergeQueueId":"550e8400-e29b-41d4-a716-446655440010","duplicatePairId":"550e8400-e29b-41d4-a716-446655440001","primaryRecordId":"550e8400-e29b-41d4-a716-446655440002","secondaryRecordId":"550e8400-e29b-41d4-a716-446655440003"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Duplicate pair not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"DUPLICATE_PAIR_NOT_FOUND","message":"Duplicate pair not found: 550e8400-e29b-41d4-a716-446655440001"}}}}},"409":{"description":"Conflict - pair already merged or in progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"already_merged":{"summary":"Already merged","value":{"success":false,"error":{"code":"PAIR_ALREADY_MERGED","message":"Duplicate pair has already been merged with status 'merged'"}}},"in_progress":{"summary":"Execution in progress","value":{"success":false,"error":{"code":"EXECUTION_IN_PROGRESS","message":"Merge execution is already in progress for this duplicate pair"}}}}}}}}}},"/../health":{"get":{"tags":["Health"],"summary":"Health Check","description":"Check the health status of the API and its dependencies. This endpoint does not require authentication.","operationId":"healthCheck","security":[],"responses":{"200":{"description":"API is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"},"example":{"success":true,"data":{"status":"healthy","timestamp":"2024-01-15T10:30:00.000Z","version":"1.0.0","requestId":"req_abc123","checks":{"database":{"status":"healthy","latencyMs":5}}}}}}}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key as Bearer token. Format: `Bearer attio_pk_your_api_key_here`"},"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key in X-API-Key header. Format: `attio_pk_your_api_key_here`"}},"schemas":{"TriggerScanRequest":{"type":"object","required":["workspaceId"],"properties":{"workspaceId":{"type":"string","format":"uuid","description":"The workspace ID to scan for duplicates"},"objectType":{"type":"string","enum":["people","companies"],"default":"companies","description":"Type of objects to scan"},"matcherTypes":{"type":"array","items":{"type":"string","enum":["similar_domain","similar_email"]},"description":"Specific matcher types to use for detection"},"minConfidenceScore":{"type":"number","minimum":0,"maximum":1,"description":"Minimum confidence score to include in results"},"maxResults":{"type":"integer","minimum":1,"description":"Maximum number of duplicate pairs to return"},"skipExisting":{"type":"boolean","default":true,"description":"Skip pairs that already exist in the database"},"storeDuplicates":{"type":"boolean","default":true,"description":"Store found duplicates in the database"}}},"TriggerScanResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"runId":{"type":"string","description":"Trigger.dev run ID for tracking progress"},"workspaceId":{"type":"string","format":"uuid","description":"The workspace being scanned"},"objectType":{"type":"string","description":"Object type being scanned"},"message":{"type":"string","description":"Status message"}}}}},"DuplicatePair":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the duplicate pair"},"workspaceId":{"type":"string","format":"uuid","description":"The workspace this pair belongs to"},"record1Id":{"type":"string","format":"uuid","description":"ID of the first record"},"record2Id":{"type":"string","format":"uuid","description":"ID of the second record"},"objectType":{"type":"string","enum":["people","companies"],"description":"Type of records"},"confidenceScore":{"type":"number","minimum":0,"maximum":1,"description":"Confidence score indicating likelihood of being duplicates"},"matchingFields":{"type":"array","items":{"type":"string"},"description":"Fields that matched between records"},"status":{"type":"string","enum":["pending","confirmed","rejected","deferred","merged","auto_merged"],"description":"Current status of the duplicate pair"},"detectedAt":{"type":"string","format":"date-time","description":"When the duplicate was detected"},"reviewedAt":{"type":"string","format":"date-time","nullable":true,"description":"When the pair was reviewed"},"reviewedBy":{"type":"string","nullable":true,"description":"User ID who reviewed the pair"},"notes":{"type":"string","nullable":true,"description":"Notes about the duplicate pair"},"deferredUntil":{"type":"string","format":"date-time","nullable":true,"description":"If deferred, when to re-evaluate"},"overrideReason":{"type":"string","nullable":true,"description":"Reason for status override"},"record1":{"type":"object","nullable":true,"properties":{"data":{"type":"object","description":"Record data fields"},"attioRecordId":{"type":"string","description":"Attio record ID"}}},"record2":{"type":"object","nullable":true,"properties":{"data":{"type":"object","description":"Record data fields"},"attioRecordId":{"type":"string","description":"Attio record ID"}}}}},"DuplicatePairsResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/DuplicatePair"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"ExecuteMergeRequest":{"type":"object","required":["duplicatePairId"],"properties":{"duplicatePairId":{"type":"string","format":"uuid","description":"The ID of the duplicate pair to merge"},"primaryRecordId":{"type":"string","format":"uuid","description":"Override which record to keep (primary). Defaults to record1."},"secondaryRecordId":{"type":"string","format":"uuid","description":"Override which record to merge into primary. Defaults to record2."},"mergeStrategy":{"type":"object","additionalProperties":{"type":"string"},"description":"Field-level merge decisions. Keys are field names, values are strategies like 'keep_primary', 'keep_secondary', etc."},"priority":{"type":"string","enum":["low","normal","high","urgent"],"default":"normal","description":"Priority of the merge operation"}}},"ExecuteMergeResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"message":{"type":"string","description":"Status message"},"runId":{"type":"string","description":"Trigger.dev run ID for tracking progress"},"mergeQueueId":{"type":"string","format":"uuid","description":"Merge queue item ID"},"duplicatePairId":{"type":"string","format":"uuid","description":"The duplicate pair being merged"},"primaryRecordId":{"type":"string","format":"uuid","description":"The record that will be kept"},"secondaryRecordId":{"type":"string","format":"uuid","description":"The record that will be merged into primary"}}}}},"HealthResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","unhealthy"],"description":"Overall health status"},"timestamp":{"type":"string","format":"date-time","description":"Current server time"},"version":{"type":"string","description":"API version"},"requestId":{"type":"string","description":"Unique request identifier"},"checks":{"type":"object","properties":{"database":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","unhealthy","unknown"]},"latencyMs":{"type":"integer","description":"Database response latency in milliseconds"}}}}}}}}},"Pagination":{"type":"object","properties":{"page":{"type":"integer","description":"Current page number"},"pageSize":{"type":"integer","description":"Number of items per page"},"total":{"type":"integer","description":"Total number of items"},"totalPages":{"type":"integer","description":"Total number of pages"}}},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"details":{"type":"object","description":"Additional error details"}}}}}}}}