{"info":{"name":"Emailchaser API","description":"The Emailchaser REST API. Set the apiKey variable to an API key from the Emailchaser app (API & MCP page). Docs: https://www.emailchaser.com/docs","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json"},"auth":{"type":"bearer","bearer":[{"key":"token","value":"{{apiKey}}","type":"string"}]},"variable":[{"key":"baseUrl","value":"https://api.emailchaser.com/r","type":"string"},{"key":"apiKey","value":"","type":"string"}],"item":[{"name":"Audience","description":"Count how many people match your targeting before you spend anything. Free, and never uses credits.","item":[{"name":"Count how many people match targeting criteria (free)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/audience/size","host":["{{baseUrl}}"],"path":["audience","size"]},"description":"Returns the number of prospects matching the given titles, seniorities, industries, company sizes and locations. Searching is free and never spends credits; only revealing contact details is metered.","body":{"mode":"raw","raw":"{\n  \"companySizes\": [\n    \"11-50\",\n    \"51-200\"\n  ],\n  \"industries\": [\n    \"Software\"\n  ],\n  \"keywords\": [\n    \"b2b saas\"\n  ],\n  \"locations\": [\n    \"United States\"\n  ],\n  \"seniorities\": [\n    \"owner\",\n    \"director\"\n  ],\n  \"titles\": [\n    \"CEO\",\n    \"Head of Sales\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]}]},{"name":"Autopilot","description":"Plan and run Autopilot: describe who you sell to and a budget, approve the plan, and Emailchaser builds and runs the outbound for you.","item":[{"name":"Size an autopilot budget plan","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/autopilot/plan","host":["{{baseUrl}}"],"path":["autopilot","plan"]},"description":"Turns a monthly budget into a concrete outbound setup: domains, mailboxes, sending volume, prospects and costs. expectedMeetingsPerMonth is a planning ESTIMATE on pessimistic funnel assumptions, not a promise. Changes nothing, but needs a read and write API key.","body":{"mode":"raw","raw":"{\n  \"budgetUsd\": 500,\n  \"maxMailboxes\": 20,\n  \"platformUsd\": 99\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"List autopilot runs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/autopilot/runs","host":["{{baseUrl}}"],"path":["autopilot","runs"],"query":[{"key":"limit","value":"","description":"Page size (default 50, max 200)","disabled":true},{"key":"page","value":"","description":"Page number, 1-based (default 1)","disabled":true}]},"description":"Lists the workspace's autopilot runs, newest first."},"response":[]},{"name":"Start an autopilot run","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/autopilot/runs","host":["{{baseUrl}}"],"path":["autopilot","runs"]},"description":"Creates an autopilot run for the given website. The run works autonomously up to the approval gate: it builds the customer profile, drafts the sequence and checks, without spending credits, that prospects match the profile. Nothing is spent, bought or sent before a human approves the run: no credits are reserved and no prospect is revealed until then, and approving is what reveals the first batch of prospects. When budgetUsd is given the sized budget plan is returned and snapshotted on the run, and targetProspects defaults to the plan's monthly prospect capacity. The plan's domain and mailbox counts are a ceiling, not a quote: one infrastructure order buys at most 10 domains, can place fewer mailboxes than the plan, and buys nothing when the workspace already has a connected sending account. Its dollar figures are an estimate, and an order is charged at the prices in force when it is placed. replyMode controls how inbound replies are handled (off, draft, approve, auto; default draft); auto sends AI reply drafts for interested replies without review. Available on every plan, and requires an active subscription. A workspace may have only one run awaiting approval at a time, and a limited number of starts per day.","body":{"mode":"raw","raw":"{\n  \"budgetUsd\": 500,\n  \"maxMailboxes\": 20,\n  \"replyMode\": \"draft\",\n  \"targetProspects\": 500,\n  \"website\": \"https://acme.com\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get an autopilot run","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/autopilot/runs/:id","host":["{{baseUrl}}"],"path":["autopilot","runs",":id"],"variable":[{"key":"id","value":"","description":"Run ID"}]},"description":"Returns one run, including its audit trail. Runs belonging to other workspaces are reported as not found."},"response":[]},{"name":"Approve an autopilot run","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/autopilot/runs/:id/approve","host":["{{baseUrl}}"],"path":["autopilot","runs",":id","approve"],"variable":[{"key":"id","value":"","description":"Run ID"}]},"description":"Approves a run that is awaiting approval, recording who approved it. Approval authorises the run's spend: the run moves to sourcing_prospects and reveals its first batch of prospects into the campaign, which spends prospect credits, then moves to provisioning_infrastructure to buy the budgeted sending infrastructure (if any), and then launches unattended. Nothing is spent, bought or sent before this call. A batch that delivers no prospects fails the run before any infrastructure is bought."},"response":[]},{"name":"Kill an autopilot run","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/autopilot/runs/:id/kill","host":["{{baseUrl}}"],"path":["autopilot","runs",":id","kill"],"variable":[{"key":"id","value":"","description":"Run ID"}]},"description":"Engages the kill switch: the run halts wherever it is and cannot be resumed, and a campaign the run launched is paused so nothing more sends. The optional reason is recorded in the audit trail.","body":{"mode":"raw","raw":"{\n  \"reason\": \"customer requested stop\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Pause an autopilot run","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/autopilot/runs/:id/pause","host":["{{baseUrl}}"],"path":["autopilot","runs",":id","pause"],"variable":[{"key":"id","value":"","description":"Run ID"}]},"description":"Suspends a run that is still in flight. A paused run can be resumed; pausing never skips the approval gate. A completed or failed run cannot be paused."},"response":[]},{"name":"Resume an autopilot run","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/autopilot/runs/:id/resume","host":["{{baseUrl}}"],"path":["autopilot","runs",":id","resume"],"variable":[{"key":"id","value":"","description":"Run ID"}]},"description":"Resumes a paused run. A run paused before approval goes back to awaiting approval — resuming can never skip the gate. A failed run resumes to the stage it failed from once the cause (e.g. insufficient credits) is fixed."},"response":[]}]},{"name":"Blocklist","description":"Addresses and domains that must never be emailed, for one workspace or for the whole account.","item":[{"name":"List blocklist entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/blocklist","host":["{{baseUrl}}"],"path":["blocklist"],"query":[{"key":"scope","value":"","description":"Which list to return: workspace, global, or all (default all)","disabled":true},{"key":"search","value":"","description":"Case-insensitive substring match on the domain or email address","disabled":true},{"key":"limit","value":"","description":"Page size (default 100, max 1000)","disabled":true},{"key":"offset","value":"","description":"Offset (default 0)","disabled":true}]},"description":"Returns the suppression entries (blocked domains and email addresses) that apply to the calling key's workspace, newest first. By default that is both lists: the workspace's own entries and the account-wide ones inherited from the main workspace. Narrow with `scope=workspace` or `scope=global`. Each item reports which list it came from."},"response":[]},{"name":"Add blocklist entries","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/blocklist","host":["{{baseUrl}}"],"path":["blocklist"]},"description":"Adds up to 1000 suppression entries in one call. Each value is either a full email address (contains \"@\") or a bare domain. Existing entries and invalid values are skipped, so the call is safe to retry and suited to carrying over a suppression list from another sending platform. `scope` defaults to \"workspace\", which suppresses for the calling key's workspace only; \"global\" suppresses across every workspace on the account and requires a main workspace's key.","body":{"mode":"raw","raw":"{\n  \"scope\": \"workspace\",\n  \"values\": [\n    \"competitor.com\",\n    \"jane@acme.com\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Delete blocklist entries","request":{"method":"DELETE","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/blocklist","host":["{{baseUrl}}"],"path":["blocklist"]},"description":"Removes up to 1000 entries in one call, by id or by value. Values are matched case-insensitively against both the domain and the email-address column, so unblocking \"acme.com\" removes the domain entry, not the individual addresses on it. `scope` limits the delete to one list and defaults to \"workspace\"; \"all\" covers both, and anything touching the account-wide list requires a main workspace's key. Entries that match nothing the caller may delete are counted as skipped rather than failing the call.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    123\n  ],\n  \"scope\": \"workspace\",\n  \"values\": [\n    \"competitor.com\",\n    \"jane@acme.com\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a blocklist entry","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/blocklist/:id","host":["{{baseUrl}}"],"path":["blocklist",":id"],"variable":[{"key":"id","value":"","description":"Blocklist entry ID"}]},"description":"Returns one suppression entry by id. Entries inherited from the account-wide list are visible here too, and report scope \"global\"."},"response":[]},{"name":"Update a blocklist entry","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/blocklist/:id","host":["{{baseUrl}}"],"path":["blocklist",":id"],"variable":[{"key":"id","value":"","description":"Blocklist entry ID"}]},"description":"Changes the blocked value, the scope, or both. A value containing \"@\" is stored as an email address, otherwise as a domain, so an entry can be converted between the two. Moving an entry to or from the account-wide list requires a main workspace's key, and moves the entry onto the main workspace.","body":{"mode":"raw","raw":"{\n  \"scope\": \"global\",\n  \"value\": \"competitor.com\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Delete a blocklist entry","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/blocklist/:id","host":["{{baseUrl}}"],"path":["blocklist",":id"],"variable":[{"key":"id","value":"","description":"Blocklist entry ID"}]},"description":"Removes one suppression entry, unblocking the domain or address. A sub-workspace key cannot delete an account-wide entry it merely inherits."},"response":[]}]},{"name":"Campaigns","description":"Create, read, schedule, launch, pause and delete campaigns, and manage the emails and mailboxes each one uses.","item":[{"name":"List campaigns","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/campaigns","host":["{{baseUrl}}"],"path":["campaigns"],"query":[{"key":"page","value":"","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"","description":"Page size (default: 20, maximum: 200)","disabled":true},{"key":"status","value":"","description":"Filter by status","disabled":true},{"key":"folderId","value":"","description":"Filter by folder ID","disabled":true}]},"description":"Lists all campaigns for the authenticated user's space with optional filtering by status and folder. Supports pagination."},"response":[]},{"name":"Create a campaign","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/campaigns","host":["{{baseUrl}}"],"path":["campaigns"]},"description":"Creates a campaign in the API key's workspace. The campaign starts in draft status and is placed in the workspace's default folder. Before it can be launched with POST /campaigns/{id}/resume, it needs four things: a sequence (PUT /campaigns/{id}/sequence), sending days (PUT /campaigns/{id}/schedule; there is no default), at least one email account (POST /campaigns/{id}/sender-emails), and at least one lead (POST /leads with campaignId). Campaigns with flow api can launch without leads.","body":{"mode":"raw","raw":"{\n  \"allowNonBusinessEmails\": true,\n  \"dailyLimit\": 1,\n  \"emoji\": \"string\",\n  \"flow\": \"multiple_leads_scheduled\",\n  \"ignoreOutOfOfficeReplies\": true,\n  \"isEnabledCatchallValidated\": true,\n  \"isEnabledEmailVerifier\": true,\n  \"isEnabledIgnoreHardBouncedLeads\": true,\n  \"isEnabledIgnoreLeadsWhoAlreadyResponded\": true,\n  \"isEnabledLlm\": true,\n  \"isEnabledSkipLeadIfAlreadyExists\": true,\n  \"isEnabledStopFollowUpsForSameCompany\": true,\n  \"isEnabledStopFollowUpsOnReply\": true,\n  \"maximumSendingLimitPerSenderEmail\": 1,\n  \"maximumSendingLimitPerSenderEmailVariation\": 123,\n  \"maximumTimeBetweenEmails\": 2,\n  \"minimumHealthScore\": 80,\n  \"minimumTimeBetweenEmails\": 2,\n  \"name\": \"Jane Doe\",\n  \"timezone\": \"America/New_York\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a campaign by ID","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/campaigns/:id","host":["{{baseUrl}}"],"path":["campaigns",":id"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Retrieves detailed information about a specific campaign including settings, schedule, statistics, and email counts. schedule.daysSchedule is one comma-separated string of weekday numbers, such as \"1,2,3,4,5\", not an array. minimumHealth lists each connected email account on the campaign with its health score and whether the campaign's minimum health score (settings.minimumHealthScore) holds it back; when every account is held back, allHeldBack is true and message says the campaign is sending nothing."},"response":[]},{"name":"Update a campaign by ID","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/campaigns/:id","host":["{{baseUrl}}"],"path":["campaigns",":id"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Updates campaign properties such as name, emoji, timezone, and various settings. Only provided fields will be updated. minimumHealthScore (1-100) stops any email account whose health score is below it, or that has no score yet, from sending in this campaign until its score is back up; its conversations wait for it and never move to another account. Send 0 to remove the minimum.","body":{"mode":"raw","raw":"{\n  \"allowNonBusinessEmails\": true,\n  \"dailyLimit\": 1,\n  \"emoji\": \"string\",\n  \"ignoreOutOfOfficeReplies\": true,\n  \"isEnabledCatchallValidated\": true,\n  \"isEnabledEmailVerifier\": true,\n  \"isEnabledIgnoreHardBouncedLeads\": true,\n  \"isEnabledIgnoreLeadsWhoAlreadyResponded\": true,\n  \"isEnabledLlm\": true,\n  \"isEnabledSkipLeadIfAlreadyExists\": true,\n  \"isEnabledStopFollowUpsAcrossCampaigns\": true,\n  \"isEnabledStopFollowUpsForSameCompany\": true,\n  \"isEnabledStopFollowUpsOnReply\": true,\n  \"maximumSendingLimitPerSenderEmail\": 1,\n  \"maximumSendingLimitPerSenderEmailVariation\": 123,\n  \"maximumTimeBetweenEmails\": 2,\n  \"minimumHealthScore\": 80,\n  \"minimumTimeBetweenEmails\": 2,\n  \"name\": \"Jane Doe\",\n  \"timezone\": \"America/New_York\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Delete a campaign","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/campaigns/:id","host":["{{baseUrl}}"],"path":["campaigns",":id"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Deletes a campaign and all associated data including emails, sequences, and scheduled tasks."},"response":[]},{"name":"Move a lead to another campaign","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/campaigns/:id/leads/:leadId/move","host":["{{baseUrl}}"],"path":["campaigns",":id","leads",":leadId","move"],"variable":[{"key":"id","value":"","description":"Source campaign ID"},{"key":"leadId","value":"","description":"Lead ID"}]},"description":"Removes a lead from the source campaign (deleting its unsent emails) and adds it to the target campaign. If the target campaign is running, emails for the lead are enqueued.","body":{"mode":"raw","raw":"{\n  \"targetCampaignId\": 123\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Pause a campaign","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/campaigns/:id/pause","host":["{{baseUrl}}"],"path":["campaigns",":id","pause"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Pauses a running campaign and cancels all scheduled emails."},"response":[]},{"name":"Launch or resume a campaign","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/campaigns/:id/resume","host":["{{baseUrl}}"],"path":["campaigns",":id","resume"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Launches a new campaign or resumes a paused campaign. Validates campaign configuration before launching: the campaign needs sending days, at least one email account, a sequence, and, unless its flow is api, at least one lead. A missing one is refused with 400 and an error naming it."},"response":[]},{"name":"Update campaign schedule","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/campaigns/:id/schedule","host":["{{baseUrl}}"],"path":["campaigns",":id","schedule"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Updates the sending schedule for a campaign including timezone, days, time windows, and frequency. daysSchedule is sent as an array of days, but the response returns it as one comma-separated string of weekday numbers, such as \"1,2,3,4,5\". GET /campaigns/{id} returns it the same way.","body":{"mode":"raw","raw":"{\n  \"daysSchedule\": [\n    \"1\",\n    \"2\",\n    \"3\",\n    \"4\",\n    \"5\"\n  ],\n  \"endSchedule\": \"string\",\n  \"everySchedule\": 1,\n  \"maximumTimeBetweenEmails\": 2,\n  \"minimumTimeBetweenEmails\": 2,\n  \"sendAt\": \"string\",\n  \"startSchedule\": \"string\",\n  \"timezone\": \"America/New_York\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Attach sender emails to a campaign","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/campaigns/:id/sender-emails","host":["{{baseUrl}}"],"path":["campaigns",":id","sender-emails"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Attaches connected sender emails from your workspace to a campaign. A running campaign only sends through the sender emails attached to it, so this call decides which mailboxes the campaign uses. Idempotent: already-attached senders are left untouched and only missing ones are added. The call is refused whole when any requested sender email is not yours (404) or not connected (409). Attaching to a running campaign triggers a reschedule so the new senders are picked up.","body":{"mode":"raw","raw":"{\n  \"senderEmailIds\": [\n    1,\n    2\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Detach a sender email from a campaign","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/campaigns/:id/sender-emails/:senderEmailId","host":["{{baseUrl}}"],"path":["campaigns",":id","sender-emails",":senderEmailId"],"variable":[{"key":"id","value":"","description":"Campaign ID"},{"key":"senderEmailId","value":"","description":"Sender Email ID"}]},"description":"Detaches a sender email from a campaign. A running campaign only sends through the sender emails attached to it, so after this call the campaign stops sending through this mailbox: unsent follow-ups to emails this sender already sent are canceled, the sender is removed from the campaign's unsent emails, and a running campaign is rescheduled onto its remaining senders. Emails already sent are untouched. Idempotent: detaching a sender that is not attached is a no-op."},"response":[]},{"name":"Get a campaign's sequence","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/campaigns/:id/sequence","host":["{{baseUrl}}"],"path":["campaigns",":id","sequence"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Returns the ordered emails that make up a campaign's sequence, including any A/B variants. delayDays on each step is counted from the previous step; the first step is always 0."},"response":[]},{"name":"Replace a campaign's sequence","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/campaigns/:id/sequence","host":["{{baseUrl}}"],"path":["campaigns",":id","sequence"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Replaces the campaign's whole sequence with the steps provided. The operation is idempotent, so sending the same payload twice leaves the same state and a retried request cannot duplicate steps. delayDays is counted from the previous step and the first step must be 0. Editing a running campaign is allowed and re-queues future sends; already-sent emails are untouched. Merge tags take ONE brace, e.g. {first_name}, {last_name}, {company_name}, {job_title}, plus any key set in a lead's customVariables; a fallback is {first_name}[there] or {first_name|there}. Placeholders written in another tool's syntax ({{first_name}}, [[first_name]], [First Name]) are rejected with a 400 that names the tag to write instead, because they are not substituted and would be sent exactly as typed (\"Hi [First Name],\").","body":{"mode":"raw","raw":"{\n  \"signature\": \"string\",\n  \"steps\": [\n    {\n      \"body\": \"string\",\n      \"contentType\": \"html\",\n      \"delayDays\": 123,\n      \"order\": 1,\n      \"subject\": \"Quick question about Acme\",\n      \"useSameThread\": true,\n      \"variants\": [\n        {\n          \"body\": \"string\",\n          \"label\": \"string\",\n          \"subject\": \"Quick question about Acme\"\n        }\n      ]\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get campaign performance stats","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/campaigns/:id/stats","host":["{{baseUrl}}"],"path":["campaigns",":id","stats"],"variable":[{"key":"id","value":"","description":"Campaign ID"}]},"description":"Returns campaign performance in one call: totals, per-A/B-variant results, per-sequence-step results, and a daily (UTC) time series. Sent and bounced count outbound emails; replied and positive count distinct leads; totals.meetings counts the campaign's leads with a booked meeting recorded (see POST /leads/{id}/meeting). Variant sent counts initial emails only, while steps attribute replies to the exact email that was answered. There is no opens metric anywhere in this response: Emailchaser does not track opens, so its absence is deliberate, not an omission."},"response":[]}]},{"name":"Copilot","description":"Plan a whole campaign from your website and a description of your buyer, then launch it in one call.","item":[{"name":"Launch a draft outbound campaign and start lead finding","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/copilot/launch","host":["{{baseUrl}}"],"path":["copilot","launch"]},"description":"Creates a DRAFT Emailchaser campaign (with its email sequence) scoped to the caller's workspace. With a Sales Navigator search URL it also kicks off lead extraction; without one the draft holds the emails and leads can be added from Lead Finder or a CSV upload. salesNavData needs a salesNavSearchUrl. The campaign stays in DRAFT — nothing sends automatically.","body":{"mode":"raw","raw":"{\n  \"icp\": {\n    \"companySizes\": [\n      \"string\"\n    ],\n    \"icpName\": \"Mid-market SaaS RevOps leaders\",\n    \"industries\": [\n      \"string\"\n    ],\n    \"personas\": [\n      {\n        \"seniority\": \"VP\",\n        \"title\": \"VP of Sales\"\n      }\n    ],\n    \"seniorities\": [\n      \"string\"\n    ],\n    \"suggestedSalesNavKeywords\": [\n      \"string\"\n    ],\n    \"summary\": \"string\",\n    \"titles\": [\n      \"string\"\n    ]\n  },\n  \"name\": \"Acme outbound Q3\",\n  \"salesNavData\": \"string\",\n  \"salesNavSearchUrl\": \"https://www.linkedin.com/sales/search/people?...\",\n  \"sequence\": [\n    {\n      \"body\": \"Hi {first_name}, ...\",\n      \"followUpAfter\": 3,\n      \"subject\": \"quick question about {company_name}\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Plan an outbound campaign from a website","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/copilot/plan","host":["{{baseUrl}}"],"path":["copilot","plan"]},"description":"Fetches the company website, infers an Ideal Customer Profile with the managed AI model, and drafts a suggested cold-email sequence. Changes nothing, but needs a read and write API key. A website that shows a placeholder page instead of the business (a parked or not yet connected domain, a default server page) is refused with 422; send a description of what you sell to plan from it instead.","body":{"mode":"raw","raw":"{\n  \"context\": \"we sell to dental clinics in the US\",\n  \"description\": \"Bookkeeping and payroll for independent restaurants\",\n  \"website\": \"https://acme.com\"\n}","options":{"raw":{"language":"json"}}}},"response":[]}]},{"name":"Credits","description":"Your credit balance, every credit transaction, and buying more credits with the card on file.","item":[{"name":"Get the credit balance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/credits/balance","host":["{{baseUrl}}"],"path":["credits","balance"]},"description":"Returns the workspace's credit wallet. A wallet is created empty on first use, so a new workspace sees zeros rather than an error; a workspace on a free trial sees its one-time trial credits, which its first balance read hands over. unlimited is true when Emailchaser has made this workspace's credits free: credit actions then never spend available and are never refused for lack of credits, so there is no need to check the balance or buy credits."},"response":[]},{"name":"Buy credits (charges real money)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/credits/purchase","host":["{{baseUrl}}"],"path":["credits","purchase"]},"description":"CHARGES REAL MONEY: this immediately charges the workspace's saved default payment method (the card behind the active subscription), off-session, with no confirmation step beyond this call. Buys between 1000 and 10000 prospect credits at the tiered list price ($33 per 1000 credits, $20 per 1000 from 5000 credits) and grants them to the wallet on success. Responds with the credits bought, the exact amount charged in USD and the new available balance. Errors carry a machine-readable code: billing_required (402, no active subscription or saved card - fix billing in the app first), payment_failed (402, the charge was refused - no money moved), credits_free (409, this workspace's credits are free, so nothing is charged and there is nothing to buy), invalid_request (400), temporarily_unavailable (503, the purchase stopped before any charge - safe to retry), purchase_incomplete (500, charged but not credited, support already notified - do NOT retry) and purchase_unconfirmed (500, check /credits/transactions before retrying). The grant is idempotent on the Stripe invoice, so one charge can never double-credit - but every successful call is a NEW charge.","body":{"mode":"raw","raw":"{\n  \"credits\": 5000\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"List credit transactions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/credits/transactions","host":["{{baseUrl}}"],"path":["credits","transactions"],"query":[{"key":"limit","value":"","description":"Page size (default 50, max 200)","disabled":true},{"key":"page","value":"","description":"Page number, 1-based (default 1)","disabled":true},{"key":"kind","value":"","description":"Filter by movement type: grant, topup, reserve, commit, refund, expire or adjustment","disabled":true},{"key":"reason","value":"","description":"Filter by reason: monthly_grant, prospect_reveal, ai_icp, ai_sequence, ai_reply, stripe_topup or manual_adjustment","disabled":true},{"key":"since","value":"","description":"Only entries at or after this time, RFC3339 or YYYY-MM-DD","disabled":true},{"key":"until","value":"","description":"Only entries at or before this time, RFC3339 or YYYY-MM-DD (a bare date means midnight UTC at the start of that day)","disabled":true}]},"description":"Lists the workspace's credit ledger entries, newest first. Optional filters: kind (movement type), reason (what the credits were for), and since/until on the entry time (both inclusive, RFC3339 or YYYY-MM-DD where a bare date means midnight UTC at the start of that day). Without filters the full ledger is returned as before. An entry with free=true was written while the workspace's credits were free: its amount is what the action would have cost, and no credits moved."},"response":[]}]},{"name":"Done For You","description":"Search and check domains, then order ready-to-send domains and mailboxes and track the order.","item":[{"name":"Check one exact domain","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/dfy/domains/check?domain=","host":["{{baseUrl}}"],"path":["dfy","domains","check"],"query":[{"key":"domain","value":"","description":"The full domain to check, extension included","disabled":false}]},"description":"Checks whether one specific domain can be registered, rather than searching the names the registrar suggests. Use this to buy a domain the customer chose. Only .com and .org are supported.\n\nA domain that simply cannot be bought - taken, malformed, an extension we do not register - answers 200 with available false and a reason. A non-2xx means the check itself could not be made, which is not the same as the domain being taken."},"response":[]},{"name":"Check a list of exact domains","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/dfy/domains/check","host":["{{baseUrl}}"],"path":["dfy","domains","check"]},"description":"Checks up to 100 exact domains in one call, for a caller who already has the list. One answer per unique domain, in the order given, each following the single check's rules. A registrar failure on one name marks it unconfirmed rather than failing the list; the call fails only when nothing could be checked.","body":{"mode":"raw","raw":"{\n  \"domains\": [\n    \"acme-outreach.com\",\n    \"acme-outreach.org\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Search available domains","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/dfy/domains/search?query=","host":["{{baseUrl}}"],"path":["dfy","domains","search"],"query":[{"key":"query","value":"","description":"Brand name to derive domains from","disabled":false},{"key":"tlds","value":"","description":"Comma-separated TLDs (default: com,org)","disabled":true},{"key":"limit","value":"","description":"Maximum suggestions to return","disabled":true}]},"description":"Checks availability and pricing of domains derived from a brand name. Only .com and .org are supported."},"response":[]},{"name":"List done-for-you orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/dfy/orders","host":["{{baseUrl}}"],"path":["dfy","orders"],"query":[{"key":"limit","value":"","description":"Page size (default 50, max 200)","disabled":true},{"key":"page","value":"","description":"Page number, 1-based (default 1)","disabled":true}]},"description":"Lists the workspace's done-for-you orders, newest first. Each order carries the same fields as GET /dfy/orders/{id}: status, cost breakdown and the caller-supplied order shape; internal billing and provider sub-records are never exposed."},"response":[]},{"name":"Create a done-for-you order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/dfy/orders","host":["{{baseUrl}}"],"path":["dfy","orders"]},"description":"Orders domains and pre-warmed mailboxes for the caller's workspace. The order is accepted and provisioned asynchronously; poll GET /dfy/orders/{id} for progress.\n\nThis call buys domains and charges the card, and it can take longer than the connection is held open — a timeout or a 502 does NOT mean the order failed. Send an Idempotency-Key header and repeat the identical request to find out: a repeat of a key that already placed an order returns that order instead of buying anything again. Reuse the key to retry safely; use a NEW key only when you intend a genuinely different order.\n\nWithout a key the order is still checked against the workspace's live orders, and one that would buy a domain or a mailbox address already bought is refused with 409 and the id of the order that has it.","body":{"mode":"raw","raw":"{\n  \"domains\": [\n    {\n      \"domainName\": \"acme-mail.com\"\n    }\n  ],\n  \"forwardingDomain\": \"acme.com\",\n  \"mailboxes\": [\n    {\n      \"domainName\": \"acme-mail.com\",\n      \"firstName\": \"John\",\n      \"lastName\": \"Doe\",\n      \"profilePicture\": \"string\",\n      \"username\": \"john\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a done-for-you order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/dfy/orders/:id","host":["{{baseUrl}}"],"path":["dfy","orders",":id"],"variable":[{"key":"id","value":"","description":"Order ID"}]},"description":"Returns one order. Orders belonging to other workspaces are reported as not found."},"response":[]}]},{"name":"Email Verification","description":"Verify a list of email addresses in bulk, with no campaign and no sending, and download the results.","item":[{"name":"List email verification jobs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/email-verification/jobs","host":["{{baseUrl}}"],"path":["email-verification","jobs"],"query":[{"key":"page","value":"1","description":"Page number, from 1","disabled":true},{"key":"size","value":"25","description":"Page size, max 200","disabled":true},{"key":"search","value":"","description":"Filter by job name","disabled":true}]},"description":"Returns the workspace's standalone verification jobs, newest first, with live counts and what each has billed."},"response":[]},{"name":"Verify a list of email addresses (spends money)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/email-verification/jobs","host":["{{baseUrl}}"],"path":["email-verification","jobs"]},"description":"SPENDS MONEY: every address checked adds metered usage to the workspace's next invoice. No campaign is created and nothing is sent. Each check runs up to two providers in a waterfall and every provider that returns a verdict is one billable verification credit, so an address costs ONE credit when the first provider rejects it outright and TWO otherwise. Call GET /email-verification/rates for the current per-credit price and use per_address_ceiling_usd to budget. An address that gets no verdict (a provider failure, a cancelled job, or the workspace's monthly verification allowance running out mid-job) is NOT billed and comes back with result unknown, still in the download. Duplicates are removed and unparseable entries are returned in invalid_emails before anything is charged. Requires an active or trialing subscription: a past_due workspace is refused with subscription_not_active, and a trialing workspace may submit up to 100 addresses in total across all its jobs, after which it is refused with trial_allowance_exceeded. Errors carry a machine-readable code: insufficient_allowance (402, with needed_usd and available_usd), trial_allowance_exceeded (402, with allowance and remaining), subscription_not_active (402), no_active_subscription (402) and invalid_request (400).","body":{"mode":"raw","raw":"{\n  \"emails\": [\n    \"ada@example.com\",\n    \"grace@example.com\"\n  ],\n  \"name\": \"Q3 conference list\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get an email verification job","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/email-verification/jobs/:id","host":["{{baseUrl}}"],"path":["email-verification","jobs",":id"],"variable":[{"key":"id","value":"","description":"Job ID"}]},"description":"Returns one job's progress and what it has billed so far. Poll this to know when state becomes done."},"response":[]},{"name":"Cancel an email verification job","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/email-verification/jobs/:id/cancel","host":["{{baseUrl}}"],"path":["email-verification","jobs",":id","cancel"],"variable":[{"key":"id","value":"","description":"Job ID"}]},"description":"Stops a running job. Addresses already verified stay in the download and stay billed; addresses that never ran are never charged, so cancelling costs nothing further and needs no refund."},"response":[]},{"name":"Download an email verification job as CSV","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/email-verification/jobs/:id/csv","host":["{{baseUrl}}"],"path":["email-verification","jobs",":id","csv"],"query":[{"key":"result","value":"","description":"Filter by verdict","disabled":true},{"key":"columns","value":"","description":"Comma-separated subset of columns, in order. Defaults to all: Email, Result, First check result, Second check result, Status, Verification Credits, Error, Verified At.","disabled":true}],"variable":[{"key":"id","value":"","description":"Job ID"}]},"description":"Streams one row per submitted address. Every address is in the file, including the ones that came back unknown because the monthly allowance ran out: they are marked rather than dropped, so the file never has a silent hole in it."},"response":[]},{"name":"List an email verification job's results","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/email-verification/jobs/:id/records","host":["{{baseUrl}}"],"path":["email-verification","jobs",":id","records"],"query":[{"key":"page","value":"1","description":"Page number, from 1","disabled":true},{"key":"size","value":"25","description":"Page size, max 200","disabled":true},{"key":"result","value":"","description":"Filter by verdict","disabled":true}],"variable":[{"key":"id","value":"","description":"Job ID"}]},"description":"Returns one row per address with its verdict and what it billed. Filter with result: valid, catchall_validated, invalid, unknown, or deliverable (valid plus catch-all, which is what a campaign will send to)."},"response":[]},{"name":"Get the email verification rate","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/email-verification/rates","host":["{{baseUrl}}"],"path":["email-verification","rates"]},"description":"Returns the current price of a verification, read from the billing plans the meter actually charges against. Read this rather than assuming a rate: an address costs one credit when the first provider rejects it outright and two when both providers answer, so per_address_ceiling_usd is the figure to budget against."},"response":[]}]},{"name":"ICPs","description":"Your ideal customer profiles: who you sell to, saved as targeting you can reuse, with a live audience size.","item":[{"name":"List Ideal Customer Profiles","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/icps","host":["{{baseUrl}}"],"path":["icps"],"query":[{"key":"limit","value":"","description":"Page size (default 50, max 200)","disabled":true},{"key":"page","value":"","description":"Page number, 1-based (default 1)","disabled":true}]},"description":"Lists the caller's workspace ICPs, newest first."},"response":[]},{"name":"Create an Ideal Customer Profile","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/icps","host":["{{baseUrl}}"],"path":["icps"]},"description":"Stores a manually-authored ICP scoped to the caller's workspace. Set makePrimary to promote it to the workspace's active profile.","body":{"mode":"raw","raw":"{\n  \"companySizes\": [\n    \"string\"\n  ],\n  \"industries\": [\n    \"string\"\n  ],\n  \"keywords\": [\n    \"string\"\n  ],\n  \"locations\": [\n    \"string\"\n  ],\n  \"makePrimary\": true,\n  \"name\": \"Mid-market SaaS RevOps leaders\",\n  \"seniorities\": [\n    \"string\"\n  ],\n  \"summary\": \"string\",\n  \"titles\": [\n    \"string\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get an Ideal Customer Profile","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/icps/:id","host":["{{baseUrl}}"],"path":["icps",":id"],"variable":[{"key":"id","value":"","description":"Profile ID"}]},"description":"Returns one profile. Profiles belonging to other workspaces are reported as not found."},"response":[]},{"name":"Update an Ideal Customer Profile","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/icps/:id","host":["{{baseUrl}}"],"path":["icps",":id"],"variable":[{"key":"id","value":"","description":"Profile ID"}]},"description":"Applies a partial edit: omitted fields are unchanged, an empty list clears the list. Any edit marks the profile as human-authored.","body":{"mode":"raw","raw":"{\n  \"companySizes\": [\n    \"string\"\n  ],\n  \"industries\": [\n    \"string\"\n  ],\n  \"keywords\": [\n    \"string\"\n  ],\n  \"locations\": [\n    \"string\"\n  ],\n  \"name\": \"Jane Doe\",\n  \"seniorities\": [\n    \"string\"\n  ],\n  \"summary\": \"string\",\n  \"titles\": [\n    \"string\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Delete an Ideal Customer Profile","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/icps/:id","host":["{{baseUrl}}"],"path":["icps",":id"],"variable":[{"key":"id","value":"","description":"Profile ID"}]},"description":"Deletes a profile. Deleting the primary leaves the workspace without one."},"response":[]},{"name":"Refresh a profile's audience size","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/icps/:id/refresh-audience-size","host":["{{baseUrl}}"],"path":["icps",":id","refresh-audience-size"],"variable":[{"key":"id","value":"","description":"Profile ID"}]},"description":"Sizes the audience matching the profile's targeting criteria against the data provider and caches the result. Sizing is free."},"response":[]},{"name":"Set the primary Ideal Customer Profile","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/icps/:id/set-primary","host":["{{baseUrl}}"],"path":["icps",":id","set-primary"],"variable":[{"key":"id","value":"","description":"Profile ID"}]},"description":"Promotes a profile to the workspace's active one, demoting any existing primary."},"response":[]},{"name":"Get the primary Ideal Customer Profile","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/icps/primary","host":["{{baseUrl}}"],"path":["icps","primary"]},"description":"Returns the workspace's active profile, or 404 when none is set."},"response":[]}]},{"name":"Imports","description":"Bring your campaigns, leads and sending accounts over from Instantly.","item":[{"name":"Import an Instantly.ai account","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/imports/instantly","host":["{{baseUrl}}"],"path":["imports","instantly"]},"description":"Validates the provided Instantly API key and enqueues a background job that imports the caller's Instantly campaigns and leads into their workspace. Every imported campaign is created in DRAFT state — nothing is sent automatically.","body":{"mode":"raw","raw":"{\n  \"apiKey\": \"insta_xxx\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"List Instantly sending accounts","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/imports/instantly/accounts","host":["{{baseUrl}}"],"path":["imports","instantly","accounts"]},"description":"Fetches the sending accounts of the given Instantly workspace and returns them together with a pre-filled CSV for the bulk IMAP/SMTP connection flow. Mailbox credentials cannot be exported from any provider, so accounts are reconnected either per-mailbox via Google/Microsoft OAuth or in bulk via IMAP app passwords using the returned CSV. Changes nothing, but needs a read and write API key.","body":{"mode":"raw","raw":"{\n  \"apiKey\": \"insta_xxx\"\n}","options":{"raw":{"language":"json"}}}},"response":[]}]},{"name":"Inbox Placement","description":"Read Inbox Placement results: where your emails land (inbox, tabs or spam) across Gmail, Outlook and Yahoo, and your senders' reputation.","item":[{"name":"Deliverability insights","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/inbox-placement/insights","host":["{{baseUrl}}"],"path":["inbox-placement","insights"],"query":[{"key":"days","value":"","description":"How many days of runs to summarise (1..365, default 30)","disabled":true}]},"description":"What is wrong across the workspace right now, worst first, each with the action that fixes it. Combines the placement measured over the window with the latest health check of every connected mailbox: authentication records, blacklist listings and measured placement per mailbox."},"response":[]},{"name":"List inbox placement runs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/inbox-placement/runs","host":["{{baseUrl}}"],"path":["inbox-placement","runs"],"query":[{"key":"testId","value":"","description":"Only runs of this test","disabled":true},{"key":"limit","value":"","description":"How many runs to return (1..200, default 50)","disabled":true}]},"description":"Lists inbox placement runs newest first, optionally filtered to one test with ?testId=. Counters on a run are only final once its status is completed. Note that every rate is -1 when nothing has been scored yet, which means NOT MEASURED and must not be rendered as 0%."},"response":[]},{"name":"Get an inbox placement run","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/inbox-placement/runs/:id","host":["{{baseUrl}}"],"path":["inbox-placement","runs",":id"],"variable":[{"key":"id","value":"","description":"Run ID"}]},"description":"Returns one run with its complete report: the headline split, the per-provider and per-sending-mailbox breakdowns, and the content spam rules the copy triggered. \"Promotions\" rolls up every Gmail category tab. \"Missing\" means the probe was never found in any folder, which usually indicates a silent block, and is reported apart from spam because it is a worse problem with a different fix."},"response":[]},{"name":"List a run's individual probes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/inbox-placement/runs/:id/results","host":["{{baseUrl}}"],"path":["inbox-placement","runs",":id","results"],"variable":[{"key":"id","value":"","description":"Run ID"}]},"description":"Returns one row per probe: which of your mailboxes sent it, which seed mailbox received it, where it landed and how long it took to arrive. deliverySeconds is worth watching on its own: greylisting and throttling show up there before they show up in a placement number."},"response":[]},{"name":"Sender reputation and health","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/inbox-placement/sender-reputation","host":["{{baseUrl}}"],"path":["inbox-placement","sender-reputation"]},"description":"The latest health check for every connected mailbox: SPF, DKIM, DMARC and MX status, blacklist listings with their delisting links, measured inbox placement, and a combined 0-100 health score. blacklistsChecked is reported next to blacklistsListed so the count can never be read as a total. A placementScore or healthScore of -1 means not measured, not zero."},"response":[]},{"name":"Inbox placement stats by date","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/inbox-placement/stats-by-date","host":["{{baseUrl}}"],"path":["inbox-placement","stats-by-date"],"query":[{"key":"from","value":"","description":"Start of the window, RFC3339. Defaults to 30 days ago.","disabled":true},{"key":"to","value":"","description":"End of the window, RFC3339. Defaults to now.","disabled":true}]},"description":"Rolls completed runs up by UTC calendar day over a window, for a trend chart. Days on which nothing ran are ABSENT from the series rather than returned as zeros: a zero-filled day plots as placement collapsing to 0%, which reads as an outage when it means nobody ran a test. Defaults to the last 30 days."},"response":[]},{"name":"List inbox placement tests","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/inbox-placement/tests","host":["{{baseUrl}}"],"path":["inbox-placement","tests"]},"description":"Lists every inbox placement test defined in the workspace, newest first. A test is the definition (content, which mailboxes to send from, and for a recurring test how often); each execution is a run, listed separately at /inbox-placement/runs."},"response":[]}]},{"name":"Lead Finder","description":"Search Emailchaser's B2B contact database for free, then add the people you want to a campaign. Adding people reveals their details and spends credits.","item":[{"name":"Get Lead Finder filter values and limits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/lead-finder/filters","host":["{{baseUrl}}"],"path":["lead-finder","filters"]},"description":"Returns the values the enum filters accept (seniorities, job functions, company sizes, revenue bands, industries, countries, headquarters countries, regions and continents), the limits that apply to this workspace (page sizes, deepest page, refs per import, first-N count, running imports, and the daily browsing allowance with what is left of it today), and creditsPerProspect, what adding one person to a campaign costs. Free: no call to the data provider is made."},"response":[]},{"name":"List Lead Finder imports","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/lead-finder/imports","host":["{{baseUrl}}"],"path":["lead-finder","imports"],"query":[{"key":"limit","value":"","description":"How many imports to return, 1 to 50 (default 10)","disabled":true}]},"description":"Lists the workspace's Lead Finder imports, newest first, with their progress. Imports keep being listed after a plan change, so a workspace can always see what it started."},"response":[]},{"name":"Add people to a campaign from the contact database (spends credits and metered verification)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/lead-finder/imports","host":["{{baseUrl}}"],"path":["lead-finder","imports"]},"description":"SPENDS CREDITS, AND BY DEFAULT METERED VERIFICATION. Reveals people from the contact database and adds them to a campaign as leads, in the background. mode=selected adds the people named by refs from search results (1 to 1,000; a ref that is no longer stored is counted in expired and dropped). mode=first_n adds up to count people matching filters who are not in the workspace yet (1 to 5,000). It continues after the last first_n import of the same filters (startsAt in the response says where; fromStart true starts from the top instead) and skips people already in the workspace for free, so repeating a first_n call adds and charges for the next count people: never repeat a call to retry, read GET /lead-finder/imports first. While a first_n import of the same filters is still running another is refused (409 import_running). It stops early when the audience runs out (endReason audience_exhausted, and the next first_n import of those filters starts from the top again) or once it has looked at five people for every one requested, at least 1,000 (endReason fetch_limit, and the next one continues from there). People already in the workspace that it passes count toward that limit although they cost nothing. Every person it looks at, added or skipped, also counts against the account's daily first_n allowance (25,000 people a UTC day by default). Each person added costs creditsPerProspect credits (1 at the time of writing). People already in the workspace, blocklisted, without a usable address, on a personal mailbox when the campaign only takes business addresses, or marked invalid by verification are skipped and cost no credits. verifyEmails (default true) checks every screened address with the paid verification waterfall of POST /email-verification/jobs, including the ones it marks invalid: one check when the first provider rejects the address and two otherwise, billed as metered usage on the next invoice at the GET /email-verification/rates price, within the account's monthly verification allowance. Catch-all and unconfirmed addresses are added and charged, and so is every address once that allowance is used up. Set verifyEmails to false to skip verification. The wallet must hold estimatedCredits for the import to start. Credits are held batch by batch and settled against the leads actually created. Leads added to a running or paused campaign get their emails at once (a paused campaign sends them when resumed); adding to a completed campaign resumes it, so it starts sending again; in a draft campaign the emails are created at launch. At most 3 imports run at once in a workspace. Poll GET /lead-finder/imports/{id} until finishedAt is set. Needs a read_write key.","body":{"mode":"raw","raw":"{\n  \"campaignId\": 123,\n  \"count\": 200,\n  \"filters\": {\n    \"cities\": [\n      \"Dublin\"\n    ],\n    \"companyKeywords\": \"string\",\n    \"companyName\": \"Acme\",\n    \"companySizes\": [\n      \"51 to 200\"\n    ],\n    \"continents\": [\n      \"string\"\n    ],\n    \"countries\": [\n      \"Ireland\"\n    ],\n    \"domains\": [\n      \"acme.com\"\n    ],\n    \"excludeCountries\": [\n      \"string\"\n    ],\n    \"excludeDomains\": [\n      \"string\"\n    ],\n    \"excludeHeadquartersCountries\": [\n      \"string\"\n    ],\n    \"excludeIndustries\": [\n      \"string\"\n    ],\n    \"excludeJobTitles\": [\n      \"Intern\"\n    ],\n    \"headquartersCountries\": [\n      \"string\"\n    ],\n    \"industries\": [\n      \"Software Development\"\n    ],\n    \"jobFunctions\": [\n      \"Sales & Business Development\"\n    ],\n    \"jobTitles\": [\n      \"Head of Sales\",\n      \"VP Sales\"\n    ],\n    \"regions\": [\n      \"string\"\n    ],\n    \"revenue\": [\n      \"string\"\n    ],\n    \"seniorities\": [\n      \"Director\"\n    ],\n    \"states\": [\n      \"string\"\n    ],\n    \"technologies\": [\n      \"HubSpot\"\n    ]\n  },\n  \"fromStart\": false,\n  \"mode\": \"selected\",\n  \"refs\": [\n    \"r_Q2x9LmVwZ3JhY2VIb3BwZX\"\n  ],\n  \"verifyEmails\": true\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a Lead Finder import","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/lead-finder/imports/:id","host":["{{baseUrl}}"],"path":["lead-finder","imports",":id"],"variable":[{"key":"id","value":"","description":"Import ID (importId)"}]},"description":"Returns one import's progress: how many people were added and skipped and why, what verification said, and the credits spent so far. Status is running, completed, failed or canceled, and added, creditsSpent and status can still change until finishedAt is set. A completed import's endReason is all_selected, requested_reached, audience_exhausted or fetch_limit; a canceled one's is canceled. A failed import has no endReason but can still have added and charged people: lastError says why it stopped (insufficient_credits, contact_cap_reached, results_expired, budget_exhausted, provider_blocked, invalid_filters, campaign_not_found or internal). A new first_n import to finish a partial one starts from the top again and counts the people already added toward its fetch limit, so it only reaches new people while those fit inside five times its count (at least 1,000); otherwise pick the missing people by ref from search results. Imports belonging to other workspaces are reported as not found."},"response":[]},{"name":"Cancel a Lead Finder import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/lead-finder/imports/:id/cancel","host":["{{baseUrl}}"],"path":["lead-finder","imports",":id","cancel"],"variable":[{"key":"id","value":"","description":"Import ID (importId)"}]},"description":"Marks a running import canceled at once and stops it after the batch it is working on. A batch the worker is already writing is still added and charged; a batch still being screened or verified is dropped, though verification checks already made are billed. Credits held for the rest are released. Until finishedAt is set, added, creditsSpent and even status can still change: if that batch was the import's last, it ends completed or failed instead. A canceled import cannot be resumed. Canceling an import that has already finished changes nothing and returns it as it is. Needs a read_write key."},"response":[]},{"name":"Search the contact database (free, masked results)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/lead-finder/searches","host":["{{baseUrl}}"],"path":["lead-finder","searches"]},"description":"Searches Emailchaser's B2B contact database with explicit filters and returns one page of matching people, masked: first name, last initial, title, seniority, job function, company name, industry, size and revenue band, a company logo (logoUrl, an image this API serves, whose URL does not contain the company's domain), and city, state and country. No email address, email or company domain, LinkedIn URL or phone number is ever returned: contact details are revealed only by adding people to a campaign with POST /lead-finder/imports. Searching spends no credits, but every result row counts against the account's daily browsing allowance (limits.rowsPerDay in GET /lead-finder/filters, 2,000 rows a UTC day by default and 250 on a trial; rowsLeftToday shows what is left). An account may start 20 searches a minute. Searches that find nobody also draw on a bucket of 120 that refills at one every 30 seconds; while it is empty, every new search is refused with rate_limited (reason empty_searches) until the next refill, usually within 30 seconds. The same page asked for again within 10 minutes comes from cache and spends nothing. The request waits up to about six seconds: 200 means the search finished (status done, or failed with error saying why, for example rate_limited when the shared request budget is busy), 202 means it is still running, so read it with GET /lead-finder/searches/{id} using the returned searchId. total is exact when totalIsExact is true; otherwise it is a lower bound until totalStatus is done. inWorkspace marks people Lead Finder added to this workspace whose lead is still there, so adding them again would be skipped for free. Each person's ref stays valid for 60 minutes after the page it came on was last shown. A read-only key may call this endpoint.","body":{"mode":"raw","raw":"{\n  \"filters\": {\n    \"cities\": [\n      \"Dublin\"\n    ],\n    \"companyKeywords\": \"string\",\n    \"companyName\": \"Acme\",\n    \"companySizes\": [\n      \"51 to 200\"\n    ],\n    \"continents\": [\n      \"string\"\n    ],\n    \"countries\": [\n      \"Ireland\"\n    ],\n    \"domains\": [\n      \"acme.com\"\n    ],\n    \"excludeCountries\": [\n      \"string\"\n    ],\n    \"excludeDomains\": [\n      \"string\"\n    ],\n    \"excludeHeadquartersCountries\": [\n      \"string\"\n    ],\n    \"excludeIndustries\": [\n      \"string\"\n    ],\n    \"excludeJobTitles\": [\n      \"Intern\"\n    ],\n    \"headquartersCountries\": [\n      \"string\"\n    ],\n    \"industries\": [\n      \"Software Development\"\n    ],\n    \"jobFunctions\": [\n      \"Sales & Business Development\"\n    ],\n    \"jobTitles\": [\n      \"Head of Sales\",\n      \"VP Sales\"\n    ],\n    \"regions\": [\n      \"string\"\n    ],\n    \"revenue\": [\n      \"string\"\n    ],\n    \"seniorities\": [\n      \"Director\"\n    ],\n    \"states\": [\n      \"string\"\n    ],\n    \"technologies\": [\n      \"HubSpot\"\n    ]\n  },\n  \"page\": 1,\n  \"pageSize\": 25\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a Lead Finder search","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/lead-finder/searches/:id","host":["{{baseUrl}}"],"path":["lead-finder","searches",":id"],"variable":[{"key":"id","value":"","description":"Search ID (searchId)"}]},"description":"Returns a search started with POST /lead-finder/searches. While it is still running the request waits up to about six seconds for it to finish before answering, so calling it again right away is fine. A finished search's results can be read for 10 minutes: after that the search reads as status failed with error expired, and after 15 minutes it is not found. Reading a finished page again within those 10 minutes keeps its people's refs valid for another 60 minutes. Searches belonging to other workspaces are reported as not found."},"response":[]}]},{"name":"Leads","description":"Add leads in bulk (up to 1,000 per request), read, update and delete them, and read a lead's conversation.","item":[{"name":"List leads","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/leads","host":["{{baseUrl}}"],"path":["leads"],"query":[{"key":"page","value":"","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"","description":"Page size (default: 20, maximum: 200)","disabled":true},{"key":"email","value":"","description":"Filter by exact email address","disabled":true},{"key":"campaignId","value":"","description":"Filter by campaign ID","disabled":true}]},"description":"Lists the leads in the API key's workspace, newest first, with pagination. Filter by exact email address to look up a single lead, or by campaign to list only the leads in one campaign. This is how a lead created through POST /leads is found again."},"response":[]},{"name":"Create or update leads in bulk","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/leads","host":["{{baseUrl}}"],"path":["leads"]},"description":"Creates or updates up to 1,000 leads in one request. Leads are matched by email within the workspace. If a lead with that email already exists, it is updated, and every named field you leave out is cleared, so send the full lead each time. An update also clears the lead's city, country, industry and company description, which the API cannot set. middleName is only saved when a lead is created. customVariables, when sent, replaces the lead's variables; leave it out to keep them. Every lead needs a valid email: one invalid lead fails the whole request with 400, and nothing is saved. To add the leads to a campaign, pass campaignId at the top level of the request body, not inside a lead. The campaign must have at least one email account attached (POST /campaigns/{id}/sender-emails). If it has none, the leads are saved and added to the campaign, but the call fails with 500; attach an account and send the same request again. count in the response is the number of leads you sent, not the number created. Any attribute outside the named fields can be sent in customVariables and becomes a merge tag usable in email copy. PUT /leads is an alias for this endpoint and behaves identically.","body":{"mode":"raw","raw":"{\n  \"campaignId\": 123,\n  \"leads\": [\n    {\n      \"company\": \"Acme\",\n      \"customVariables\": {},\n      \"email\": \"jane@acme.com\",\n      \"firstName\": \"Jane\",\n      \"lastName\": \"Doe\",\n      \"linkedin\": \"https://www.linkedin.com/in/janedoe\",\n      \"middleName\": \"\",\n      \"phone\": \"+1 415 555 0100\",\n      \"title\": \"Head of Sales\",\n      \"website\": \"https://acme.com\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a lead by ID","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/leads/:id","host":["{{baseUrl}}"],"path":["leads",":id"],"variable":[{"key":"id","value":"","description":"Lead ID"}]},"description":"Retrieves one lead. The lead comes back as a flat object, not wrapped in a \"lead\" key. It has no middleName; GET /leads includes it."},"response":[]},{"name":"Update a lead by ID","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/leads/:id","host":["{{baseUrl}}"],"path":["leads",":id"],"variable":[{"key":"id","value":"","description":"Lead ID"}]},"description":"Updates one lead. What happens depends on whether the lead is in a campaign. For a lead in no campaign, only the fields you send are changed, and customVariables, when sent, replaces the whole set. For a lead in a campaign, the lead is saved again from your request: every named field you leave out is cleared, so send the full lead each time. This also clears the lead's city, country, industry and company description, which the API cannot set. customVariables is kept if you leave it out. Sending a different email does not change this lead: it creates a lead with the new email in the same campaign, or overwrites the lead that already has that email, and this lead stays as it was. middleName is accepted but never saved. The response holds only the lead's id, email, firstName and lastName, plus customVariables for a lead in no campaign. Read the full lead with GET /leads/{id}.","body":{"mode":"raw","raw":"{\n  \"company\": \"Acme\",\n  \"customVariables\": {},\n  \"email\": \"jane@acme.com\",\n  \"firstName\": \"Jane\",\n  \"lastName\": \"Doe\",\n  \"linkedin\": \"https://www.linkedin.com/in/janedoe\",\n  \"middleName\": \"\",\n  \"phone\": \"+1 415 555 0100\",\n  \"title\": \"Head of Sales\",\n  \"website\": \"https://acme.com\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Delete a lead by ID","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/leads/:id","host":["{{baseUrl}}"],"path":["leads",":id"],"variable":[{"key":"id","value":"","description":"Lead ID"}]},"description":"Deletes a lead and all associated unsent emails. If the lead is associated with campaigns, it will be removed from those campaigns first."},"response":[]},{"name":"Set or correct a lead's category","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/leads/:id/category","host":["{{baseUrl}}"],"path":["leads",":id","category"],"variable":[{"key":"id","value":"","description":"Lead ID"}]},"description":"Sets the lead's category (tag), typically to correct an AI misclassification of a reply. Validated against the category enum: interested, not_interested, bounced, out_of_office, delivery_incomplete, meeting_booked. Setting meeting_booked behaves exactly like POST /leads/{id}/meeting: it records meetingBookedAt when unset and keeps an existing one, because a meeting is an explicit mark set by you or your agent, never inferred by the system. A real category change fires the LeadCategoryUpdate webhook; setting the value the lead already has is a no-op. Changing the category away from meeting_booked does NOT clear the booked-meeting mark - use DELETE /leads/{id}/meeting for that.","body":{"mode":"raw","raw":"{\n  \"category\": \"interested\"\n}","options":{"raw":{"language":"json"}}}},"response":[]}]},{"name":"Meetings","description":"Mark a lead as having booked a meeting, or take the mark off.","item":[{"name":"Mark a meeting as booked on a lead","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/leads/:id/meeting","host":["{{baseUrl}}"],"path":["leads",":id","meeting"],"variable":[{"key":"id","value":"","description":"Lead ID"}]},"description":"Records that a meeting was booked with this lead. A meeting is an explicit mark set by you or your agent when a call actually gets scheduled - Emailchaser never infers one from reply text or calendars, so this call (or PUT /leads/{id}/category with meeting_booked, which behaves identically) is the only way a meeting is recorded. Sets meetingBookedAt to now, sets the lead's category to meeting_booked, and fires the LeadCategoryUpdate webhook on the category change. Idempotent: repeating the call keeps the original meetingBookedAt and fires no second webhook. Marked meetings are counted in GET /campaigns/{id}/stats totals.meetings and GET /reports/outcomes."},"response":[]},{"name":"Unmark a booked meeting on a lead","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/leads/:id/meeting","host":["{{baseUrl}}"],"path":["leads",":id","meeting"],"variable":[{"key":"id","value":"","description":"Lead ID"}]},"description":"Removes the explicit booked-meeting mark: clears meetingBookedAt and, when the lead's category is still meeting_booked, reverts it to interested (firing the LeadCategoryUpdate webhook on the change). A category that was changed to something else since booking is left alone. Idempotent: unmarking a lead with no meeting is a no-op."},"response":[]}]},{"name":"Prospecting","description":"Fill a campaign with people from Emailchaser's contact database who match an Ideal Customer Profile. Revealing them spends credits.","item":[{"name":"Fill a campaign with prospects from the contact database","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/prospects/source","host":["{{baseUrl}}"],"path":["prospects","source"]},"description":"Queues a background job that searches Emailchaser's internal contact database with the targeting criteria of an Ideal Customer Profile, reveals matching people and adds them to the campaign as leads. The profile defaults to the workspace's primary ICP when icpId is omitted. Sourcing is asynchronous: poll GET /leads?campaignId= to watch the prospects arrive. Each stored prospect costs the reveal price in credits (1 at the time of writing), reported per request as creditsPerProspect with the batch ceiling as estimatedCredits; duplicates, blocklisted domains and contacts without an email address are filtered out before any credit is spent. Repeated calls for the same campaign and profile resume the same search at its provider cursor, so they page deeper into the audience instead of re-revealing (and re-paying for) the same people.","body":{"mode":"raw","raw":"{\n  \"campaignId\": 123,\n  \"count\": 50,\n  \"icpId\": 7\n}","options":{"raw":{"language":"json"}}}},"response":[]}]},{"name":"Replies","description":"Read what prospects wrote back, then edit and send the reply drafts Emailchaser wrote for them.","item":[{"name":"Get a lead's conversation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/leads/:id/conversation","host":["{{baseUrl}}"],"path":["leads",":id","conversation"],"variable":[{"key":"id","value":"","description":"Lead ID"}]},"description":"Returns the lead's full email thread in chronological order: outbound emails (sent, scheduled, and unsent drafts) and inbound replies. Each item carries direction (inbound/outbound), status, and, for categorized inbound emails, the AI response category. Unsent drafts, including AI-suggested replies awaiting human review, are marked with isDraft. An AI reply draft (isDraft true, campaignId null) can be edited with PUT /reply-drafts/{id} and sent with POST /reply-drafts/{id}/send, using its id from this list. Drafts that belong to a campaign cannot be sent this way."},"response":[]},{"name":"List replies","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/replies","host":["{{baseUrl}}"],"path":["replies"],"query":[{"key":"category","value":"","description":"Filter by response category (interested, not_interested, wrong_person, bounced, out_of_office, delivery_incomplete, dmarc_report, mixmax, warmup_email, unsubscribe, newsletter)","disabled":true},{"key":"campaignId","value":"","description":"Filter by campaign ID","disabled":true},{"key":"leadId","value":"","description":"Filter by lead ID","disabled":true},{"key":"since","value":"","description":"Only replies received at or after this time (RFC3339 or YYYY-MM-DD)","disabled":true},{"key":"page","value":"","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"","description":"Page size (default: 20, maximum: 200)","disabled":true}]},"description":"Lists inbound emails (replies received from prospects) in the API key's workspace, newest first, 20 per page by default (?limit= up to 200). Filter by AI response category, campaign, lead, or a time floor. responseCategory is null while AI categorization is still pending; campaignId is null for standalone replies that could not be attributed to a campaign. Read-only: replying happens by editing the AI draft (PUT /reply-drafts/{id}) and sending it (POST /reply-drafts/{id}/send) - the draft's current subject and body are what goes out."},"response":[]},{"name":"List AI reply drafts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/reply-drafts","host":["{{baseUrl}}"],"path":["reply-drafts"],"query":[{"key":"page","value":"","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"","description":"Page size (default: 20, maximum: 200)","disabled":true}]},"description":"Lists AI-suggested reply drafts awaiting human review in the API key's workspace, newest first, 20 per page by default (?limit= up to 200). Each draft answers the inbound reply referenced by inReplyToEmailId, and to and cc show who it goes to when sent: Reply All, so everyone the prospect addressed is copied. Drafts can be edited over REST (PUT /reply-drafts/{id}) and sent (POST /reply-drafts/{id}/send) - both require the read_write scope."},"response":[]},{"name":"Edit an AI reply draft","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/reply-drafts/:id","host":["{{baseUrl}}"],"path":["reply-drafts",":id"],"variable":[{"key":"id","value":"","description":"Reply draft ID"}]},"description":"Updates the subject and/or body of an AI reply draft awaiting human review. At least one field must be provided and provided fields must be non-empty. This endpoint never changes the draft's status or sends anything - use POST /reply-drafts/{id}/send once the wording is ready. Emails that are not AI reply drafts - sent or scheduled emails, inbound replies, campaign sequence templates - are refused with 409.","body":{"mode":"raw","raw":"{\n  \"body\": \"Thanks for getting back to me - would Tuesday work for a quick call?\",\n  \"subject\": \"Re: Quick question\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Send an AI reply draft","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/reply-drafts/:id/send","host":["{{baseUrl}}"],"path":["reply-drafts",":id","send"],"variable":[{"key":"id","value":"","description":"Reply draft ID"}]},"description":"Schedules an AI reply draft for delivery through the conversation's sender mailbox, in the same email thread. It goes out as Reply All: to the person who wrote the reply, with everyone else they addressed (their To and Cc lines, minus your workspace's own mailboxes) in Cc, as listed in to and cc. The draft's current subject and body are what goes out, so edit first (PUT /reply-drafts/{id}) if needed. Emails that are not sendable AI reply drafts - already sent or scheduled emails, inbound replies, campaign sequence templates - are refused with 409. A draft whose conversation has no connected sender mailbox or no resolvable recipient is refused with 422."},"response":[]}]},{"name":"Reports","description":"Put what you spent against what it produced, for any date range.","item":[{"name":"Get the money-vs-outcomes report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/reports/outcomes","host":["{{baseUrl}}"],"path":["reports","outcomes"],"query":[{"key":"since","value":"","description":"Window start, RFC3339 or YYYY-MM-DD (inclusive)","disabled":true},{"key":"until","value":"","description":"Window end, RFC3339 or YYYY-MM-DD (inclusive; a bare date means midnight UTC at the start of that day)","disabled":true},{"key":"campaignId","value":"","description":"Narrow the outcome side to one campaign (partial attribution; spend stays workspace-level)","disabled":true}]},"description":"Puts spend against results for a window (since/until compare inclusively; both optional). Spend has two parts. (1) Settled credit debits from the workspace ledger, grouped by reason; in-flight reservations are excluded until they settle, credits used while Emailchaser had made the workspace's credits free are not spend and are excluded, and credits are valued at the current list price of one credit, the same per-credit price a top-up is quoted at before volume discounts, regardless of what was actually paid for them via bulk discounts or plan allowances. (2) Done-for-you order costs from each order's stored cost breakdown; orders are counted by creation time, and failed or canceled orders are excluded because their charges are unwound. Subscription (platform) fees are NOT included: the backend stores only the current subscription state, not per-window invoice history, so they cannot be attributed to a window honestly. Outcomes are sent emails, replied leads, positively-replied (interested) leads and leads marked meeting-booked in the window. costPerReply, costPerPositive and costPerMeeting divide total spend by each outcome count and are null when that count is zero. The optional campaignId narrows the OUTCOME side only - credit and order spend is workspace-level and cannot be attributed to one campaign - so per-campaign cost figures are partial attribution, not a true campaign cost."},"response":[]}]},{"name":"Sender Emails","description":"The mailboxes your campaigns send from: list and connect them, check their DNS, health and warm-up.","item":[{"name":"List sender emails","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/sender-emails","host":["{{baseUrl}}"],"path":["sender-emails"],"query":[{"key":"page","value":"","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"","description":"Page size (default: 20, maximum: 200)","disabled":true},{"key":"spaceId","value":"","description":"Filter by space ID","disabled":true},{"key":"campaignId","value":"","description":"Filter by campaign ID","disabled":true},{"key":"isConnected","value":"","description":"Filter by connection status","disabled":true}]},"description":"Lists all sender emails for the authenticated user's space with optional filtering by campaign ID and connection status. Supports pagination. Each sender email carries its health score (healthScore): 0-100, higher is better, the share of its warm-up emails over the last 7 full days that landed in the inbox rather than spam; it moves daily as warm-up emails land in the inbox (up) or in spam (down), and is null while warm-up is off or before 20 warm-up emails were checked. Use it to pick the accounts to add to a campaign."},"response":[]},{"name":"Connect an SMTP/IMAP sender email","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/sender-emails","host":["{{baseUrl}}"],"path":["sender-emails"]},"description":"Connects an SMTP/IMAP mailbox to the workspace using an app password, with no browser step. Google and Microsoft mailboxes are not supported here because both require an interactive consent screen; connect those in the app. The address must be a business domain.","body":{"mode":"raw","raw":"{\n  \"email\": \"jane@acme.com\",\n  \"firstName\": \"Jane\",\n  \"imapServerUrl\": \"string\",\n  \"lastName\": \"Doe\",\n  \"loginString\": \"string\",\n  \"password\": \"string\",\n  \"smtpServerUrl\": \"string\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a sender email by ID","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/sender-emails/:id","host":["{{baseUrl}}"],"path":["sender-emails",":id"],"variable":[{"key":"id","value":"","description":"Sender Email ID"}]},"description":"Retrieves detailed information about a specific sender email including connection status, limits, settings, its last 20 connection events and health score. The sender email comes back as a flat object, not wrapped in a \"senderEmail\" key. The health score (healthScore) is 0-100, higher is better: the share of the account's warm-up emails over the last 7 full days that landed in the inbox rather than spam. It moves daily as warm-up emails land in the inbox (up) or in spam (down), and is null while warm-up is off or before 20 warm-up emails were checked."},"response":[]},{"name":"Update a sender email by ID","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/sender-emails/:id","host":["{{baseUrl}}"],"path":["sender-emails",":id"],"variable":[{"key":"id","value":"","description":"Sender Email ID"}]},"description":"Updates an existing sender email's information. Only provided fields will be updated. Can update name, signature, and daily sending limits.","body":{"mode":"raw","raw":"{\n  \"currentDailyLimit\": 123,\n  \"familyName\": \"string\",\n  \"givenName\": \"string\",\n  \"maximumSendingsLimitPerDay\": 1,\n  \"minimumSendingsLimitPerDay\": 1,\n  \"signature\": \"string\",\n  \"toggleGradualBuildUp\": true\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get a sender email's DNS vitals and warm-up status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/sender-emails/:id/dns","host":["{{baseUrl}}"],"path":["sender-emails",":id","dns"],"variable":[{"key":"id","value":"","description":"Sender Email ID"}]},"description":"Checks the sender domain's SPF, DKIM, DMARC and MX records and reports each as OK, WARNING, MISSING or ERROR with human-readable issues, the same checks behind the app's DNS vitals view. DNS lookups run live at request time (a few seconds worst case), so the response always reflects current records - there is no cache to go stale. Also includes the mailbox's warm-up status: whether it is enrolled, today's ramp target (currentPerDay), the configured daily cap and its health score."},"response":[]},{"name":"Get a sender email's warm-up settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/sender-emails/:id/warmup","host":["{{baseUrl}}"],"path":["sender-emails",":id","warmup"],"variable":[{"key":"id","value":"","description":"Sender Email ID"}]},"description":"Returns the mailbox's warm-up configuration: whether warm-up is on, the ramp (startLimit warm-up emails on the first day, increaseBy more each day, up to capLimit a day), weekdays-only, timezone, today's ramp target (currentPerDay) and the account's health score (healthScore, 0-100, higher is better: the share of its warm-up emails over the last 7 full days that landed in the inbox rather than spam; null while warm-up is off or before 20 were checked). Warm-up volume is separate from the campaign sending limits on the sender email. When configured is false the mailbox has never been enrolled, and the values shown are the defaults that switching warm-up on would use."},"response":[]},{"name":"Update a sender email's warm-up settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/sender-emails/:id/warmup","host":["{{baseUrl}}"],"path":["sender-emails",":id","warmup"],"variable":[{"key":"id","value":"","description":"Sender Email ID"}]},"description":"Switches warm-up on or off and changes the warm-up ramp. Only the fields provided change. Warm-up sends startLimit emails on the first day and adds increaseBy each day until it reaches capLimit a day; this volume is separate from the campaign sending limits on the sender email. Switching warm-up on for a mailbox that has never been enrolled enrols it with the defaults (2 a day, 2 more each day, up to 10 a day, weekdays only, UTC) overridden by any fields sent, and the ramp starts today; the mailbox must be connected. A mailbox that has never been enrolled needs enabled: true in the same request to take any other setting. Switching warm-up off keeps the settings and stops a reconnect from switching it back on. Raising capLimit on a mailbox that is already warming takes effect from the next scheduling run without restarting the ramp.","body":{"mode":"raw","raw":"{\n  \"capLimit\": 40,\n  \"enableReplies\": false,\n  \"enabled\": true,\n  \"increaseBy\": 2,\n  \"startLimit\": 2,\n  \"timezone\": \"America/New_York\",\n  \"weekdaysOnly\": true\n}","options":{"raw":{"language":"json"}}}},"response":[]}]},{"name":"Setup","description":"Check that an API key works. Built for AI assistants, which call it first to confirm their connection.","item":[{"name":"Confirm your AI assistant is connected","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/setup/ping","host":["{{baseUrl}}"],"path":["setup","ping"]},"description":"Called by the customer's AI assistant as its first request, to confirm the API key works. Records which assistant connected and when on the key itself, and completes the \"Set up with AI\" onboarding task for the key's workspace. Idempotent: repeat pings simply refresh the recorded assistant and timestamp. The optional agent field is the assistant's name in any form; it is normalized to a short lowercase identifier (e.g. chatgpt, claude, grok, gemini, copilot) and echoed back.","body":{"mode":"raw","raw":"{\n  \"agent\": \"string\"\n}","options":{"raw":{"language":"json"}}}},"response":[]}]},{"name":"Webhooks","description":"Register, update and delete webhook endpoints that receive Emailchaser events as they happen.","item":[{"name":"List webhooks","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/webhooks","host":["{{baseUrl}}"],"path":["webhooks"]},"description":"Lists all registered webhook endpoints for the authenticated user's space."},"response":[]},{"name":"Register a webhook","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/webhooks","host":["{{baseUrl}}"],"path":["webhooks"]},"description":"Registers a webhook endpoint that will be called when the given event type occurs (e.g. LeadCreated, CampaignStatusChanged, EmailReply). The URL must use HTTPS. A signing secret is created for the webhook, but the API does not return it. Copy it in the app under Settings, Integrations & API, on the Webhooks tab, where you can also rotate it.","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Doe\",\n  \"type\": \"string\",\n  \"url\": \"https://example.com/webhooks/emailchaser\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Update a webhook","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/webhooks/:id","host":["{{baseUrl}}"],"path":["webhooks",":id"],"variable":[{"key":"id","value":"","description":"Webhook ID"}]},"description":"Updates a registered webhook's name, URL, event type or enabled state. Only provided fields are changed. Disabling a webhook stops deliveries without deleting it.","body":{"mode":"raw","raw":"{\n  \"isEnabled\": true,\n  \"name\": \"Jane Doe\",\n  \"type\": \"string\",\n  \"url\": \"https://example.com/webhooks/emailchaser\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Delete a webhook","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/webhooks/:id","host":["{{baseUrl}}"],"path":["webhooks",":id"],"variable":[{"key":"id","value":"","description":"Webhook ID"}]},"description":"Deletes a registered webhook endpoint. Events of its type will no longer be delivered to its URL."},"response":[]}]},{"name":"Workspace","description":"Create workspaces and their API keys, read members, and set the billing profile that done-for-you orders use.","item":[{"name":"Get the workspace billing profile","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/space/billing-profile","host":["{{baseUrl}}"],"path":["space","billing-profile"]},"description":"Returns the registrant and postal contact details held for the workspace. These are the details filed with the registrar when the Done For You flow buys a domain, and the address a CAN-SPAM footer must carry. Returns 404 until a profile has been set."},"response":[]},{"name":"Set the workspace billing profile","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/space/billing-profile","host":["{{baseUrl}}"],"path":["space","billing-profile"]},"description":"Creates or replaces the workspace's registrant and postal contact details. Idempotent: sending the same body twice leaves the same state. Every field except addressLineTwo is required, because incomplete registrant details are filed with the registrar just as readily as complete ones. Set this before ordering domains through /dfy/orders.","body":{"mode":"raw","raw":"{\n  \"addressLineOne\": \"1 Example Street\",\n  \"addressLineTwo\": \"Suite 200\",\n  \"city\": \"New York\",\n  \"company\": \"Acme Ltd\",\n  \"country\": \"US\",\n  \"firstName\": \"Jane\",\n  \"lastName\": \"Doe\",\n  \"phone\": \"2125550142\",\n  \"phoneCc\": \"1\",\n  \"postalCode\": \"10001\",\n  \"state\": \"NY\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Get workspace details and members","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/space/members","host":["{{baseUrl}}"],"path":["space","members"]},"description":"Returns the workspace behind the API key: campaign counts broken down by status, and the list of members with their roles. Useful for confirming which workspace a key belongs to."},"response":[]},{"name":"List workspaces","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/workspaces","host":["{{baseUrl}}"],"path":["workspaces"]},"description":"Lists the workspaces owned by the API key's workspace owner: the main workspace and its sub-workspaces. isCurrent marks the workspace the calling key is bound to."},"response":[]},{"name":"Create a workspace","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/workspaces","host":["{{baseUrl}}"],"path":["workspaces"]},"description":"Creates a sub-workspace under the API key's main workspace and, by default, mints a read+write API key bound to the new workspace. The key's fullKey is returned exactly once and cannot be retrieved again, and it never outlives the caller: when the calling key has an expiry, the minted key carries the same expiresAt. Must be called with a main workspace's key: keys bound to sub-workspaces are refused. API keys are included in every plan; workspaces are a Professional-plan feature.","body":{"mode":"raw","raw":"{\n  \"generateApiKey\": true,\n  \"name\": \"Acme Outbound\"\n}","options":{"raw":{"language":"json"}}}},"response":[]},{"name":"Create an API key for a workspace","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/workspaces/:id/api-keys","host":["{{baseUrl}}"],"path":["workspaces",":id","api-keys"],"variable":[{"key":"id","value":"","description":"Workspace ID"}]},"description":"Mints an API key bound to a workspace the calling key already owns: its own workspace, or one of its sub-workspaces when called with a main workspace's key. The key returned by POST /r/workspaces is shown once and cannot be retrieved again, so this is how a workspace whose key was lost gets a new one without a UI step. The new key authorizes the named workspace only and never outlives the caller: when the calling key has an expiry, the minted key carries the same expiresAt. fullKey is returned exactly once.","body":{"mode":"raw","raw":"{\n  \"name\": \"VoiceDrop outbound key\",\n  \"readOnly\": false\n}","options":{"raw":{"language":"json"}}}},"response":[]}]}]}