Guffles

Customer API

Connect your tools to the leads, lists and jobs in your workspace. API access is available on every plan, including trial.

Authentication

Create a key in Settings → Account & security. Save it when it is shown, and choose only the permissions your tool needs. Revoke it there at any time.

GET /api/v1/leads?limit=50&status=good
Authorization: Bearer YOUR_API_KEY

All data belongs to the key’s workspace. Keys are hashed at rest. Session cookies do not authenticate API requests.

Credits and retries

Jobs follow the dashboard’s credit, cache and refund rules. Search reserves credits at creation; verification and enrichment reserve them when the worker claims a batch. Quotes are free.

Send an Idempotency-Key header for each new mutation. Reuse it when retrying the same request. Changed input returns 409. Poll the returned statusUrl for the durable result.

Limits are 60 requests per key and 300 per workspace each minute. Honor Retry-After on 429. Pages use limit (1–100) and offset; inspect hasMore. JSON bodies are limited to 262144 bytes.

Webhooks

Register an HTTPS receiver and choose events in Settings → Integrations. Payloads contain IDs, status and counts. Fetch full records through this API.

Verify the exact body with the timestamp and HMAC headers before processing. Receivers without a crypto node can forward those bytes and headers to the authenticated verification endpoint. Deduplicate effects by event ID, and acknowledge with 2xx after the effect commits.

Delivery logs include eight attempts with retry backoff. Ten consecutive exhausted deliveries disable an endpoint. Test ping and terminal redelivery use no credits. Logs are retained for 31 days.

Resources

Successful JSON responses wrap the result in data. Pages also include total, limit, offset and hasMore. Exports return a saved selection and an authenticated CSV download URL.

GET/api/v1/outreach/campaignsRead outreach campaigns list

Metadata and status only, under an explicit outreach:read grant. Existing keys do not gain this permission. Campaigns include saved drafts; drafts are not launched. Conversation IDs are opaque strings scoped to the campaign and must be URL encoded. Messages are ordered by due time and ID; accepted means Gmail accepted the send, not inbox delivery. bookingConfirmed records acknowledged initial event creation; meetingState and changeStatus describe subsequent changes. Offered links are not bookings. Full contact data requires leads:read. Gmail text, campaign briefs, review evidence and provider credentials are excluded. Sending, approval and Calendar changes stay in the app.

Permissions: outreach:read.

GET/api/v1/outreach/campaigns/{campaignId}Read outreach campaigns get

Metadata and status only, under an explicit outreach:read grant. Existing keys do not gain this permission. Campaigns include saved drafts; drafts are not launched. Conversation IDs are opaque strings scoped to the campaign and must be URL encoded. Messages are ordered by due time and ID; accepted means Gmail accepted the send, not inbox delivery. bookingConfirmed records acknowledged initial event creation; meetingState and changeStatus describe subsequent changes. Offered links are not bookings. Full contact data requires leads:read. Gmail text, campaign briefs, review evidence and provider credentials are excluded. Sending, approval and Calendar changes stay in the app.

Permissions: outreach:read.

GET/api/v1/outreach/campaigns/{campaignId}/conversationsRead outreach conversations list

Metadata and status only, under an explicit outreach:read grant. Existing keys do not gain this permission. Campaigns include saved drafts; drafts are not launched. Conversation IDs are opaque strings scoped to the campaign and must be URL encoded. Messages are ordered by due time and ID; accepted means Gmail accepted the send, not inbox delivery. bookingConfirmed records acknowledged initial event creation; meetingState and changeStatus describe subsequent changes. Offered links are not bookings. Full contact data requires leads:read. Gmail text, campaign briefs, review evidence and provider credentials are excluded. Sending, approval and Calendar changes stay in the app.

Permissions: outreach:read.

GET/api/v1/outreach/campaigns/{campaignId}/conversations/{conversationId}Read outreach conversations get

Metadata and status only, under an explicit outreach:read grant. Existing keys do not gain this permission. Campaigns include saved drafts; drafts are not launched. Conversation IDs are opaque strings scoped to the campaign and must be URL encoded. Messages are ordered by due time and ID; accepted means Gmail accepted the send, not inbox delivery. bookingConfirmed records acknowledged initial event creation; meetingState and changeStatus describe subsequent changes. Offered links are not bookings. Full contact data requires leads:read. Gmail text, campaign briefs, review evidence and provider credentials are excluded. Sending, approval and Calendar changes stay in the app.

Permissions: outreach:read.

GET/api/v1/outreach/campaigns/{campaignId}/conversations/{conversationId}/messagesRead outreach messages list

Metadata and status only, under an explicit outreach:read grant. Existing keys do not gain this permission. Campaigns include saved drafts; drafts are not launched. Conversation IDs are opaque strings scoped to the campaign and must be URL encoded. Messages are ordered by due time and ID; accepted means Gmail accepted the send, not inbox delivery. bookingConfirmed records acknowledged initial event creation; meetingState and changeStatus describe subsequent changes. Offered links are not bookings. Full contact data requires leads:read. Gmail text, campaign briefs, review evidence and provider credentials are excluded. Sending, approval and Calendar changes stay in the app.

Permissions: outreach:read.

GET/api/v1/outreach/campaigns/{campaignId}/meetingsRead outreach meetings list

Metadata and status only, under an explicit outreach:read grant. Existing keys do not gain this permission. Campaigns include saved drafts; drafts are not launched. Conversation IDs are opaque strings scoped to the campaign and must be URL encoded. Messages are ordered by due time and ID; accepted means Gmail accepted the send, not inbox delivery. bookingConfirmed records acknowledged initial event creation; meetingState and changeStatus describe subsequent changes. Offered links are not bookings. Full contact data requires leads:read. Gmail text, campaign briefs, review evidence and provider credentials are excluded. Sending, approval and Calendar changes stay in the app.

Permissions: outreach:read.

GET/api/v1/outreach/activityRead outreach activity list

Metadata and status only, under an explicit outreach:read grant. Existing keys do not gain this permission. Campaigns include saved drafts; drafts are not launched. Conversation IDs are opaque strings scoped to the campaign and must be URL encoded. Messages are ordered by due time and ID; accepted means Gmail accepted the send, not inbox delivery. bookingConfirmed records acknowledged initial event creation; meetingState and changeStatus describe subsequent changes. Offered links are not bookings. Full contact data requires leads:read. Gmail text, campaign briefs, review evidence and provider credentials are excluded. Sending, approval and Calendar changes stay in the app.

Permissions: outreach:read.

GET/api/v1/webhook-eventsRead committed webhook event metadata

Newest first, with optional event type filtering. Returns the immutable IDs, status and counts sent to subscribed endpoints, retained for 31 days. Test pings are excluded. Events before the workspace had a matching active subscriber are not backfilled. It exposes no signing secret, callback URL, delivery state or full lead data.

Permissions: webhooks:read.

GET/api/v1/webhooks/{endpointId}/deliveriesRead delivery and attempt logs

Read delivery and attempt logs

Permissions: webhooks:read.

POST/api/v1/webhooks/{endpointId}/pingQueue a signed test ping

Queued through the transactional outbox. Requires the current endpoint revision and an active grant. Replaying the same request queues nothing extra. Controls share a workspace budget of 20 per minute. Redelivery preserves eventId and creates a new delivery with eight attempts; receivers still deduplicate by eventId. Pending or running deliveries cannot be manually redelivered.

Permissions: webhooks:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "expectedRevision": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    }
  },
  "required": [
    "expectedRevision"
  ],
  "additionalProperties": false
}
POST/api/v1/webhooks/{endpointId}/deliveries/{deliveryId}/redeliverRedeliver a terminal event

Queued through the transactional outbox. Requires the current endpoint revision and an active grant. Replaying the same request queues nothing extra. Controls share a workspace budget of 20 per minute. Redelivery preserves eventId and creates a new delivery with eight attempts; receivers still deduplicate by eventId. Pending or running deliveries cannot be manually redelivered.

Permissions: webhooks:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "expectedRevision": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    }
  },
  "required": [
    "expectedRevision"
  ],
  "additionalProperties": false
}
POST/api/v1/webhooks/{endpointId}/verifyVerify exact raw webhook bytes

Send the exact raw body and X-Guffles-Delivery-Id, X-Guffles-Timestamp and X-Guffles-Signature headers. The helper checks a fresh HMAC timestamp against an owned current delivery without revealing the signing secret. It does not acknowledge processing; callers must make their subsequent side effects idempotent. At most 4096 raw bytes. Duplicate valid verification is allowed for retrying the receiver workflow.

Permissions: webhooks:read.

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "type": {
      "type": "string",
      "enum": [
        "lead.created",
        "list.updated",
        "verification.completed",
        "verification.failed",
        "enrichment.completed",
        "enrichment.failed",
        "search.completed",
        "search.failed",
        "mission.completed",
        "reply.received",
        "meeting.booked",
        "meeting.cancelled",
        "lead.unsubscribed",
        "lead.closed",
        "webhook.test"
      ]
    },
    "version": {
      "type": "string",
      "const": "v1"
    },
    "occurredAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
    },
    "data": {
      "type": "object",
      "properties": {
        "leadId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "listId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "jobId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "searchId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "missionId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "campaignId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "conversationId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 320
        },
        "bookingId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "changeId": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "closureReason": {
          "type": "string",
          "enum": [
            "not_interested",
            "no_response",
            "no_budget",
            "wrong_person",
            "left_company",
            "meeting_booked",
            "meeting_cancelled",
            "unsubscribed",
            "do_not_contact",
            "bounced",
            "other"
          ]
        },
        "status": {
          "type": "string",
          "maxLength": 40
        },
        "counts": {
          "type": "object",
          "propertyNames": {
            "type": "string",
            "maxLength": 40,
            "pattern": "^[a-zA-Z]+$"
          },
          "additionalProperties": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        }
      },
      "additionalProperties": false
    }
  },
  "required": [
    "id",
    "type",
    "version",
    "occurredAt",
    "data"
  ],
  "additionalProperties": false
}
GET/api/v1/webhooksList webhook endpoints

Optional exact name and status filters. Deleted metadata is excluded unless includeDeleted=true; it never reveals callback paths or signing secrets. Newest first. Tombstone IDs can distinguish a new subscription from a retry of an earlier creation.

Permissions: webhooks:read.

GET/api/v1/webhooks/{endpointId}Read webhook endpoint metadata

Read webhook endpoint metadata

Permissions: webhooks:read.

POST/api/v1/webhooksWebhooks: create

HTTPS public endpoints only, with DNS checked at registration and each delivery. URLs and signing secrets are encrypted. Read responses show only the URL origin. Create or explicit secret rotation returns signingSecret; a creation retry can recover it only for the original caller and unchanged revision. Receipts contain metadata only. Update requires expectedRevision; URL changes and re-enabling rotate the secret. Revoking an API key pauses its bound endpoints. Deletion pauses all future delivery. Limit: 20 registered endpoints per workspace. Outreach subscriptions include reply.received, meeting.booked, meeting.cancelled, lead.unsubscribed and lead.closed; committed events carry IDs and bounded metadata only. Deletion uses an empty body.

Permissions: webhooks:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "name": {
      "default": "Webhook endpoint",
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "url": {
      "type": "string",
      "maxLength": 2000,
      "format": "uri"
    },
    "events": {
      "minItems": 1,
      "maxItems": 14,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "lead.created",
          "list.updated",
          "verification.completed",
          "verification.failed",
          "enrichment.completed",
          "enrichment.failed",
          "search.completed",
          "search.failed",
          "mission.completed",
          "reply.received",
          "meeting.booked",
          "meeting.cancelled",
          "lead.unsubscribed",
          "lead.closed"
        ]
      }
    }
  },
  "required": [
    "url",
    "events"
  ],
  "additionalProperties": false
}
PATCH/api/v1/webhooks/{endpointId}Webhooks: update

HTTPS public endpoints only, with DNS checked at registration and each delivery. URLs and signing secrets are encrypted. Read responses show only the URL origin. Create or explicit secret rotation returns signingSecret; a creation retry can recover it only for the original caller and unchanged revision. Receipts contain metadata only. Update requires expectedRevision; URL changes and re-enabling rotate the secret. Revoking an API key pauses its bound endpoints. Deletion pauses all future delivery. Limit: 20 registered endpoints per workspace. Outreach subscriptions include reply.received, meeting.booked, meeting.cancelled, lead.unsubscribed and lead.closed; committed events carry IDs and bounded metadata only. Deletion uses an empty body.

Permissions: webhooks:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "expectedRevision": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "url": {
      "type": "string",
      "maxLength": 2000,
      "format": "uri"
    },
    "events": {
      "minItems": 1,
      "maxItems": 14,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "lead.created",
          "list.updated",
          "verification.completed",
          "verification.failed",
          "enrichment.completed",
          "enrichment.failed",
          "search.completed",
          "search.failed",
          "mission.completed",
          "reply.received",
          "meeting.booked",
          "meeting.cancelled",
          "lead.unsubscribed",
          "lead.closed"
        ]
      }
    },
    "enabled": {
      "type": "boolean"
    },
    "rotateSigningSecret": {
      "type": "boolean"
    }
  },
  "required": [
    "expectedRevision"
  ],
  "additionalProperties": false
}
DELETE/api/v1/webhooks/{endpointId}Webhooks: delete

HTTPS public endpoints only, with DNS checked at registration and each delivery. URLs and signing secrets are encrypted. Read responses show only the URL origin. Create or explicit secret rotation returns signingSecret; a creation retry can recover it only for the original caller and unchanged revision. Receipts contain metadata only. Update requires expectedRevision; URL changes and re-enabling rotate the secret. Revoking an API key pauses its bound endpoints. Deletion pauses all future delivery. Limit: 20 registered endpoints per workspace. Outreach subscriptions include reply.received, meeting.booked, meeting.cancelled, lead.unsubscribed and lead.closed; committed events carry IDs and bounded metadata only. Deletion uses an empty body.

Permissions: webhooks:write.

Requires Idempotency-Key. Use a new key for new work.

GET/api/v1/meRead the key's workspace and permissions

Read the key's workspace and permissions

Permissions: Any active workspace API key.

GET/api/v1/leadsList and filter owned leads

List and filter owned leads

Permissions: leads:read.

GET/api/v1/leads/{leadId}Read a prepared lead

Read a prepared lead

Permissions: leads:read.

GET/api/v1/listsList workspace lists

List workspace lists

Permissions: lists:read.

GET/api/v1/lists/{listId}Read a list

Read a list

Permissions: lists:read.

GET/api/v1/lists/{listId}/membersList member IDs

List member IDs

Permissions: lists:read.

POST/api/v1/listsLists: create

Members must already belong to this workspace. This operation does not import leads or grant entitlements. A list referenced by a Finder job cannot be deleted (409).

Permissions: lists:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}
PATCH/api/v1/lists/{listId}Lists: update

Members must already belong to this workspace. This operation does not import leads or grant entitlements. A list referenced by a Finder job cannot be deleted (409).

Permissions: lists:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}
DELETE/api/v1/lists/{listId}Lists: delete

Members must already belong to this workspace. This operation does not import leads or grant entitlements. A list referenced by a Finder job cannot be deleted (409).

Permissions: lists:write.

Requires Idempotency-Key. Use a new key for new work.

POST/api/v1/lists/{listId}/membersLists: members.add

Members must already belong to this workspace. This operation does not import leads or grant entitlements. A list referenced by a Finder job cannot be deleted (409).

Permissions: lists:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "leadIds": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    }
  },
  "required": [
    "leadIds"
  ],
  "additionalProperties": false
}
DELETE/api/v1/lists/{listId}/membersLists: members.remove

Members must already belong to this workspace. This operation does not import leads or grant entitlements. A list referenced by a Finder job cannot be deleted (409).

Permissions: lists:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "leadIds": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    }
  },
  "required": [
    "leadIds"
  ],
  "additionalProperties": false
}
POST/api/v1/searchesStart a Finder workflow

Uses the existing credit reservation. Optional verification, enrichment and list writes also require their respective write scopes. Business filters use display country/state names (United States, Texas); specify category or keyword. People filters use jobTitle and optional location, seniority, industry, size and emailStatus. Quotes/credits and complete followup status follow the dashboard rules. An enabled async Finder rollout is required.

Permissions: searches:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "source": {
      "type": "string",
      "enum": [
        "businesses",
        "people"
      ]
    },
    "filters": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "requestedCount": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5000
    },
    "workflow": {
      "type": "object",
      "properties": {
        "verificationEnabled": {
          "type": "boolean"
        },
        "enrichmentEnabled": {
          "type": "boolean"
        },
        "enrichmentScope": {
          "type": "string",
          "enum": [
            "all",
            "deliverable"
          ]
        },
        "qualificationEnabled": {
          "default": false,
          "type": "boolean"
        },
        "qualificationScope": {
          "default": "all",
          "type": "string",
          "enum": [
            "all",
            "researched",
            "verified_researched"
          ]
        },
        "listMode": {
          "type": "string",
          "enum": [
            "none",
            "existing",
            "new"
          ]
        },
        "requestedListId": {
          "anyOf": [
            {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            },
            {
              "type": "null"
            }
          ]
        },
        "newListName": {
          "anyOf": [
            {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "verificationEnabled",
        "enrichmentEnabled",
        "enrichmentScope",
        "listMode",
        "requestedListId",
        "newListName"
      ],
      "additionalProperties": false,
      "description": "none requires null requestedListId and newListName; existing requires requestedListId and null newListName; new requires newListName and null requestedListId. Disabled qualification requires scope all; researched requires enrichment; verified_researched requires verification plus deliverable enrichment."
    }
  },
  "required": [
    "source",
    "filters",
    "requestedCount",
    "workflow"
  ],
  "additionalProperties": false
}
POST/api/v1/verification/quoteQuote verification

Returns the dashboard's existing cache, eligibility and credit calculation; does not spend credits.

Permissions: verification:read.

{
  "type": "object",
  "properties": {
    "source": {
      "type": "string",
      "enum": [
        "businesses",
        "people"
      ]
    },
    "force": {
      "default": false,
      "type": "boolean"
    },
    "leadIds": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    }
  },
  "required": [
    "source",
    "leadIds"
  ],
  "additionalProperties": false
}
POST/api/v1/verification/jobsStart verification

The existing worker reserves credits when claiming a batch. Fresh cached results are free. Verification charges one credit per two distinct fresh emails, rounded up per batch. Business enrichment costs one credit; people enrichment costs two. Existing skip/refund rules apply. Verification's optional enrichment requires enrichment:write; request identities are assigned by the server.

Permissions: verification:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "source": {
      "type": "string",
      "enum": [
        "businesses",
        "people"
      ]
    },
    "force": {
      "default": false,
      "type": "boolean"
    },
    "leadIds": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    },
    "enrichment": {
      "default": {
        "enabled": false,
        "scope": "all"
      },
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "scope": {
          "type": "string",
          "enum": [
            "all",
            "deliverable"
          ]
        }
      },
      "required": [
        "enabled",
        "scope"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "source",
    "leadIds"
  ],
  "additionalProperties": false
}
POST/api/v1/enrichment/quoteQuote enrichment

Returns the dashboard's existing cache, eligibility and credit calculation; does not spend credits.

Permissions: enrichment:read.

{
  "type": "object",
  "properties": {
    "source": {
      "type": "string",
      "enum": [
        "businesses",
        "people"
      ]
    },
    "scope": {
      "type": "string",
      "enum": [
        "all",
        "deliverable"
      ]
    },
    "leadIds": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    }
  },
  "required": [
    "source",
    "scope",
    "leadIds"
  ],
  "additionalProperties": false
}
POST/api/v1/enrichment/jobsStart enrichment

The existing worker reserves credits when claiming a batch. Fresh cached results are free. Verification charges one credit per two distinct fresh emails, rounded up per batch. Business enrichment costs one credit; people enrichment costs two. Existing skip/refund rules apply. Verification's optional enrichment requires enrichment:write; request identities are assigned by the server.

Permissions: enrichment:write.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "source": {
      "type": "string",
      "enum": [
        "businesses",
        "people"
      ]
    },
    "scope": {
      "type": "string",
      "enum": [
        "all",
        "deliverable"
      ]
    },
    "leadIds": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    }
  },
  "required": [
    "source",
    "scope",
    "leadIds"
  ],
  "additionalProperties": false
}
GET/api/v1/searchesList searches

List searches

Permissions: searches:read.

GET/api/v1/searches/{jobId}Read searches status

Read searches status

Permissions: searches:read.

GET/api/v1/searches/{jobId}/resultsRead searches results

Read searches results

Permissions: searches:read, leads:read.

GET/api/v1/verification/jobsList verification/jobs

List verification/jobs

Permissions: verification:read.

GET/api/v1/verification/jobs/{jobId}Read verification/jobs status

Read verification/jobs status

Permissions: verification:read.

GET/api/v1/verification/jobs/{jobId}/itemsRead verification/jobs items

Read verification/jobs items

Permissions: verification:read, leads:read.

GET/api/v1/enrichment/jobsList enrichment/jobs

List enrichment/jobs

Permissions: enrichment:read.

GET/api/v1/enrichment/jobs/{jobId}Read enrichment/jobs status

Read enrichment/jobs status

Permissions: enrichment:read.

GET/api/v1/enrichment/jobs/{jobId}/itemsRead enrichment/jobs items

Read enrichment/jobs items

Permissions: enrichment:read, leads:read.

GET/api/v1/search-historyList saved search history

List saved search history

Permissions: searches:read.

GET/api/v1/search-history/{searchId}Read saved search history

Read saved search history

Permissions: searches:read.

GET/api/v1/search-history/{searchId}/resultsRead saved prepared results

Read saved prepared results

Permissions: searches:read, leads:read.

GET/api/v1/qualificationsRead qualification evidence

Read qualification evidence

Permissions: qualification:read.

GET/api/v1/qualifications/{qualificationId}Read a qualification result

Read a qualification result

Permissions: qualification:read.

GET/api/v1/exportsList export metadata

List export metadata

Permissions: exports:read.

GET/api/v1/exports/{exportId}Read export metadata

Read export metadata

Permissions: exports:read.

POST/api/v1/exportsSave a CSV export selection

Choose exactly one selection. listId also requires lists:read; searchId requires searches:read. The selection is saved once, export usage is recorded once and credits are unchanged. Download separately with exports:read and leads:read.

Permissions: exports:write, leads:read.

Requires Idempotency-Key. Use a new key for new work.

{
  "type": "object",
  "properties": {
    "label": {
      "default": "Guffles export",
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "leadIds": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    },
    "listId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "searchId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    }
  },
  "additionalProperties": false,
  "oneOf": [
    {
      "required": [
        "leadIds"
      ]
    },
    {
      "required": [
        "listId"
      ]
    },
    {
      "required": [
        "searchId"
      ]
    }
  ]
}
GET/api/v1/exports/{exportId}/downloadDownload an authenticated CSV

Includes all prepared fields, person data, research, full qualification reason and all three drafted emails. Removed list members remain in the saved selection. A legacy export without a saved selection returns 410; lost workspace ownership returns 409. Data failures return an error before any successful CSV bytes.

Permissions: exports:read, leads:read.

Errors and exports

Errors return an error code and an X-Request-ID. 401 means the key is invalid; 403 means a permission is missing; 402 means insufficient credits; 409 means a conflict; 429 means a limit was reached.

CSV includes prepared business and person data, verification, research, the full qualification reason and all three draft emails. A legacy export without a saved selection returns 410. An export whose leads no longer belong to the workspace returns 409. Removed list members remain in a saved export selection.

See the OpenAPI document for complete request and response schemas. Create separate keys for separate tools so permissions and revocation stay clear.