SealDeal API

A REST API over your contacts, companies, lists, deals, sequences, drafts, action items, suppression list, and platform events, plus analytics and webhooks for event driven integrations. All 58 operations are documented below. Machine readable OpenAPI 3.1 schema: /api-docs/openapi.json

Base URL: https://sealdeal.ai/api/v1

Authentication

Every request is authenticated with an API key belonging to exactly one organization. Mint keys at /admin/integrations/api-keys, choosing the scopes the key should carry. The secret is shown once at creation and stored only as a hash, so it cannot be recovered later.

curl https://sealdeal.ai/api/v1/me \
  -H "Authorization: Bearer obk_live_<your-secret>"

A missing header returns 401, an unknown or expired key returns 401, and a valid key that lacks the scope a route requires returns 403 naming the missing scope.

Scopes

ScopeGrants
contacts.readRead contacts and their research dossiers.
contacts.writeCreate, update, and soft delete contacts, convert inbound leads, act on action items.
contacts.erasePermanently and irreversibly erase a contact (GDPR Article 17). Also requires the acting key to be attributed to an org admin role (manager, admin, or owner), the scope alone is not sufficient.
lists.readRead contact lists.
lists.writeCreate, generate drafts for, and soft delete lists.
deals.readRead deals and companies.
deals.writeCreate and update deals, and move them between stages.
sequences.readRead contact sequences.
sequences.writeEnqueue and pause sequences.
drafts.readRead drafts and messages.
drafts.writeApprove and reject drafts.
drafts.sendEnqueue an approved draft for real sending.
suppression.writeAdd, look up, and remove suppression entries.
actions.readRead action items (the NBA work queue).
events.readRead the platform event log and usage counters.
analytics.readRead aggregate funnel and lift analytics.
webhooks.manageRegister, inspect, and delete webhook endpoints.

Rate limits

Pagination

List endpoints return cursor paginated results:

{
  "data": [ ... ],
  "nextCursor": "eyJpZCI6Ii4uLiIsInQiOiIyMDI2LTA1LTIzVDEwOjAwOjAwLjAwMFoifQ"
}

Pass ?cursor=<value> on the next call to fetch the following page. A null nextCursor means the last page. Default page size is 50 and the maximum is 200, set with ?limit=.

Errors

Error responses use RFC 7807 application/problem+json:

{
  "type": "https://sealdeal.ai/errors/insufficient-scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "The API key is missing required scopes: deals.write."
}

Endpoints

Identity

MethodPathScopeDescription
GET/api/v1/meany valid keyThe calling key’s organization (id and name) plus the scopes it was granted. Useful as a connection test.

Contacts

MethodPathScopeDescription
GET/api/v1/contactscontacts.readList contacts. Cursor paginated. Filters: listId, qualificationStatus, q. Each item includes deletedAt and erasedAt (both null for a live contact). Pass ?includeDeleted=true to also see soft deleted contacts, the only way to see one over this API.
POST/api/v1/contactscontacts.writeCreate a contact.
GET/api/v1/contacts/:idcontacts.readFetch a single contact. Pass ?include=research to embed the cached research dossier under a research key (null when none has been computed yet).
PATCH/api/v1/contacts/:idcontacts.writeUpdate a contact.
GET/api/v1/contacts/:id/researchcontacts.readThe cached research dossier for a contact (sources, recent news, pain points).
POST/api/v1/contacts/:id/researchcontacts.writeRefresh the research dossier for a contact.
POST/api/v1/contacts/:id/pause-sequencesequences.writePause the contact’s active outreach sequence.
DELETE/api/v1/contacts/:idcontacts.writeSoft delete a contact. Reversible, and the operation most callers want. The contact and its history are kept, it disappears from default reads and the send queue, and an admin can restore it later (undoAvailableUntil is advisory only, not a hard deadline). Idempotent. This is not the GDPR verb, see the erase action below for that.
POST/api/v1/contacts/:id/erasecontacts.erasePermanently and irreversibly erase a contact (GDPR Article 17). Requires the contacts.erase scope, and the acting API key must additionally be attributed to an org admin role (manager, admin, or owner). Holding the scope alone is not enough, a rep owned key with the scope still gets a 403. Body requires requesterRef and reason. Returns 503 if this deployment has no erasure hash secret configured.

Companies

MethodPathScopeDescription
GET/api/v1/companiesdeals.readList companies. Cursor paginated. Optional ?search= filters on name or domain.
GET/api/v1/companies/:iddeals.readFetch a single company, including industry, size, headquarters, tier, tags, and a research source count.
GET/api/v1/companies/:id/researchdeals.readThe cached research dossier for a company (sources, recent news, pain points).
POST/api/v1/companies/:id/researchdeals.writeTrigger a fresh, company seeded research run for a company (bypasses cache).

Contact lists

MethodPathScopeDescription
GET/api/v1/listslists.readList contact lists. Cursor paginated. Pass ?includeArchived=1 to include archived lists.
POST/api/v1/listslists.writeCreate a contact list.
GET/api/v1/lists/:idlists.readFetch a single contact list.
POST/api/v1/lists/:id/generatelists.writeGenerate outreach drafts for the contacts on a list.
DELETE/api/v1/lists/:idlists.writeSoft delete a list and its memberships. Contacts always survive, a contact whose only membership was this list becomes unlisted, never deleted. Returns 409 if the list has in flight sequences unless you pass ?force=true, which pauses them first instead of stranding them mid cadence. Idempotent.

Deals

Every deal (list and detail) carries forecastCategory, readinessScore, and lossReason, plus probability (always 0 to 100, never a 0 to 1 fraction). Pass ?include=meddpic on either GET to also get the qualification framework blob, since it can be large and most callers do not need it.

MethodPathScopeDescription
GET/api/v1/dealsdeals.readList deals. Cursor paginated. Filter: stageSlug. Optional ?include=meddpic.
POST/api/v1/dealsdeals.writeCreate a deal.
GET/api/v1/deals/:iddeals.readFetch a single deal. Same shape as a list item. Optional ?include=meddpic.
PATCH/api/v1/deals/:iddeals.writeUpdate a deal.
POST/api/v1/deals/:id/stagedeals.writeMove a deal to a new pipeline stage. Records the transition in stage history.
GET/api/v1/deals/:id/historydeals.readThe deal’s pipeline stage transition log, newest first. Cursor paginated.

Sequences

MethodPathScopeDescription
GET/api/v1/sequencessequences.readList contact sequences. Filters: contactId, status.
POST/api/v1/sequencessequences.writeEnqueue a sequence for a contact against a sequence template.

Drafts and messages

MethodPathScopeDescription
GET/api/v1/draftsdrafts.readList drafts. Cursor paginated. Filters: status, contactId.
GET/api/v1/drafts/:iddrafts.readFetch a single draft, including the full body and its engagement timeline. Pass ?include=evidence to add resolved citations, an unresolved citation count, the guardrail report, and the effective model, so a reviewer can see why the AI wrote what it wrote, not just what it wrote.
POST/api/v1/drafts/:id/approvedrafts.writeApprove a draft.
POST/api/v1/drafts/:id/rejectdrafts.writeReject a draft.
POST/api/v1/drafts/:id/senddrafts.sendEnqueue an approved draft’s sequence for real sending. Gated by the platform send kill switch and by per-organization transport verification.
GET/api/v1/messagesdrafts.readList inbound and outbound messages. Cursor paginated.

Inbound leads

MethodPathScopeDescription
POST/api/v1/leads/:id/convertcontacts.writeConvert a captured inbound lead into a contact (and, where policy allows, a deal).
POST/api/v1/leads/:id/spamcontacts.writeMark a captured inbound lead as spam.

Actions

Each action declares its own required scope; the key must carry that scope in addition to being valid.

MethodPathScopeDescription
POST/api/v1/actionsper actionInvoke a named capability by key. Returns 403 when the key lacks that capability’s scope.
GET/api/v1/action-itemsactions.readList action items (the NBA work queue). Cursor paginated. Filters: status, priority, entityType, entityId, salesPersonId.
GET/api/v1/action-items/:idactions.readFetch a single action item.
POST/api/v1/action-items/:id/:verbcontacts.writeApply a verb (for example complete or dismiss) to an action item.

Suppression

There is no dedicated suppression.read scope. Reading (GET) reuses suppression.write, since every caller that can suppress an address is already trusted with the lesser act of looking one up.

MethodPathScopeDescription
GET/api/v1/suppressionsuppression.writePass ?email= to look up a single address, or omit it to list the org’s suppression entries. Cursor paginated in list mode.
POST/api/v1/suppressionsuppression.writeSuppress an email address or domain so no future outreach can be sent to it. Idempotent.
DELETE/api/v1/suppressionsuppression.writeLift a suppression with ?email=. Idempotent, always 200, even when no entry existed.

Events and usage

MethodPathScopeDescription
GET/api/v1/eventsevents.readThe platform event log, cursor paginated. Filter with eventType (a single type or a comma separated list).
GET/api/v1/usageevents.readUsage counters for the organization.

Analytics

Aggregate only, GET only. No per contact or per person row is ever returned. Every numeric field names its unit (_count, _cents, _pct, _pp), and a rate is null, never 0, when its denominator is 0.

MethodPathScopeDescription
GET/api/v1/analytics/funnelanalytics.readThe outbound to pipeline funnel over a required, bounded window (?from=&to=, YYYY-MM-DD, both inclusive, maximum 366 days). Attribution is sequence grain, single attribution, so dealsAttributed can be less than dealsCreatedAllSources by design, and truncation.dealScanTruncated marks every number as a floor rather than a total when the scan limit is hit.
GET/api/v1/analytics/liftanalytics.readThe two holdout experiments (email lift and AI actions lift). Takes no date range, since a holdout experiment is measured from assignment to a pre registered horizon, not an arbitrary window. lift_pp and its confidence interval are real percentage points, converted from the underlying fraction.

Webhooks

Webhook management is how event driven integrations subscribe. See the Webhooks section below for the delivery contract.

MethodPathScopeDescription
GET/api/v1/webhookswebhooks.manageList registered webhook endpoints.
POST/api/v1/webhookswebhooks.manageRegister an endpoint. Body: url plus eventTypes (an array of event types, or ["*"] for all). Returns the signing secret once.
GET/api/v1/webhooks/eventswebhooks.manageThe catalogue of event types available to subscribe to.
GET/api/v1/webhooks/:idwebhooks.manageFetch a single webhook endpoint.
PATCH/api/v1/webhooks/:idwebhooks.manageUpdate an endpoint’s url, subscribed event types, or status.
DELETE/api/v1/webhooks/:idwebhooks.manageDelete an endpoint. Used by consumers to unsubscribe.
GET/api/v1/webhooks/:id/deliverieswebhooks.manageList delivery attempts for an endpoint, with status and response code.
GET/api/v1/webhooks/:id/deliveries/:deliveryIdwebhooks.manageFetch a single delivery attempt, including the request body that was sent.
POST/api/v1/webhooks/:id/deliveries/:deliveryId/replaywebhooks.manageReplay a previous delivery attempt.
POST/api/v1/webhooks/:id/rotate-secretwebhooks.manageRotate the signing secret. The new secret is returned once.
POST/api/v1/webhooks/:id/test-deliverwebhooks.manageSend a synthetic test payload to the endpoint and return the HTTP status and latency.

Public content

This endpoint requires no authentication and allows any origin.

MethodPathScopeDescription
GET/api/v1/public-templatesnoneActive public sequence templates. Filters: useCase, industry, sequenceStep.

Webhooks

Register an endpoint with POST /api/v1/webhooks (or from /admin/integrations/webhooks), naming the event types to subscribe to. Retrieve the catalogue of available types from GET /api/v1/webhooks/events.

Deliveries arrive as a stable JSON envelope:

{
  "apiVersion": "2026-07-21",
  "id": "evt_2a7f9c00example",
  "type": "deal.won",
  "createdAt": "2026-07-21T10:14:00.000Z",
  "organizationId": "org_example",
  "data": { "dealId": "d_1", "dealName": "Acme, Platform" }
}

Each delivery carries an X-Outbound-Signature header in the form t=<unix-seconds>,v1=<hex>. Verify it by computing HMAC-SHA256("<t>." + rawBody, signingSecret) and comparing in constant time. The signing secret is returned once when the endpoint is created and can be rotated with POST /api/v1/webhooks/:id/rotate-secret.

Failed deliveries are retried with backoff. Inspect attempts with GET /api/v1/webhooks/:id/deliveries and replay an individual attempt if needed.

Multi-tenancy

Every API key belongs to exactly one organization, and every endpoint scopes its queries by that key's organization. There is no cross-tenant access path: a key minted by one organization cannot read another's data even if it knows the other's resource ids.