← all tools
Sequencers

SourceWhale

The sequencer built for recruiters rather than for sales teams.

Cost per callfree
Your own keyrequired — contact us to enable
CategorySequencers

SourceWhale is outreach shaped the way a desk works: candidates and campaigns instead of leads and deals, with reporting that matches how you are measured. Your agent pushes the shortlist in and can start the outreach once you have approved it.

SourceWhale is activation with a recruiting shape. Sourcing and enrichment fill it.

things you can ask for

  • Push the shortlist from Vincere into the campaign and start it
  • Load the candidates GitHub surfaced, once Prospeo has their emails
  • What are my numbers this week?
  • Find this person by email and tell me where they are in the sequence

works well with SourceWhale

  • GitHub

    technical shortlists suit a recruiter-shaped sequencer

  • Prospeo

    the contact details the campaign needs

  • Vincere

    keep the desk and the outreach on one record

  • Fathom

    the interview that follows a positive reply

Ask for the outcome and your agent composes the run itself. Or start from a skill, a whole pipeline it already knows end to end.

browse all skills →

What your agent reads before it touches SourceWhale

Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.

Read the SourceWhale docs →
Show the raw playbookwritten for the agent

Routing layer for all SourceWhale public-API work. This file is WHAT/guardrails; the HOW lives in reference/endpoints.md and the Python client lib/sourcewhale.py.

1) What this skill governs

  • Routing, auth, and safety gates for SourceWhale calls.
  • Canonical execution surface: the Python client lib/sourcewhale.py (Python-only stack).
  • Per-endpoint detail lives in reference/endpoints.md.

2) Auth & config

  • Base URL: https://sourcewhale.app/public-api
  • API key: read from env SOURCEWHALE_API_KEY (SourceWhale Settings → API/Integrations).
  • User email: most endpoints take a userEmail query param ("the user performing the action"). Set env SOURCEWHALE_USER_EMAIL once and the client defaults every call to it; override per-call when acting as another user.
  • Paths CONFIRMED from the live Swagger spec (2026-06-01) — all 8 /v1/... endpoints, their query params, the candidates/search key set (socialLink|photo|email|phone), the candidate field list, and the zapier bodies match lib/sourcewhale.py exactly. Index: reference/docs/sourcewhale/raw/endpoints.md.
  • Auth CONFIRMED (2026-06-01): header api-key (raw key value, no prefix) — from the Swagger Authorize dialog (ApiKeyAuthorizer / Name: api-key / In: header). Env SOURCEWHALE_API_KEY; never hardcode.
  • Secret handling — MANDATORY: never hardcode the key in any file or commit. The client raises if SOURCEWHALE_API_KEY is missing.

3) Read behavior — before any execution

Before writing/running a call, open reference/endpoints.md for the exact params, body shape, and the candidate object fields. Don't guess field names.

⚠️ PREREQUISITE — the campaign must already exist (NO create-campaign API)

SourceWhale's public API has no endpoint to create a campaign. You can only list_campaigns (read) and add_candidates into an existing campaign. Therefore, before any candidate push:

  1. STOP and tell the user explicitly: "SourceWhale has no API to create campaigns, so you need to create the campaign (and its message steps) manually in the SourceWhale app first. Tell me once it exists." Do not attempt to create it via request() or any other call — there is no such endpoint.
  2. Wait for the user to confirm the campaign has been created in the UI.
  3. Then discover its id: list_campaigns(user_email=…) → find the campaign by name → use its id as campaign_id in add_candidates. (If list_campaigns doesn't show it, the user created it under a different userEmail — ask which user owns it.)

Only after the campaign exists and you've resolved its id may you proceed to the approval-gated push below. The same applies to message/sequence steps — those are authored in the UI, not via the API.

4) Approval gates (writes & outreach)

SourceWhale is an outreach tool — writes can trigger real messages to candidates.

  1. sendImmediately: true on add_candidates STARTS OUTREACH immediately. Treat any call with send_immediately=True as a paid/irreversible action: confirm explicitly with the user first. Default to send_immediately=False (or omit) unless told otherwise.
  2. Dry-run first. For bulk loads, add ONE candidate first and show the user the request + response before continuing.
  3. Get explicit go-ahead before adding more than one candidate or before any sendImmediately run.
  4. modify_candidate requires a valid candidateId (get it via search_candidates).

5) Operations covered

  • Campaigns: list_campaigns (optional metrics, partner filters) — read-only; no create-campaign API (see Prerequisite above: the user must create the campaign in the UI first)
  • Projects: list_projects
  • Candidates: add_candidates (into campaign/projects, optional immediate send), modify_candidate, search_candidates (by socialLink | photo | email | phone)
  • Reporting: dashboard_statistics (from/to, YYYY-MM-DD)
  • Webhooks: zapier_subscribe, zapier_unsubscribe

6) Handoff with the GTM/enrichment layer

Canonical recruiting flow: source + enrich candidates (your sourcing/enrichment adapters) into a CSV → map rows to the SourceWhale candidate object → add_candidates into a campaign. Use search_candidates to dedupe by email before adding. Inspect the CSV first; never read large CSVs into context.

Callable surface — lib/sourcewhale.py

Import: from lib.sourcewhale import SourceWhale → instantiate SourceWhale() (reads key from env). Base: https://sourcewhale.app/public-api. Generic passthrough: request(method, path, *, params, json).

  • add_candidates(candidates: list[dict], campaign_id: str | None = None, project_ids: list[str] | None = None, send_immediately: bool | None = None, user_email: str | None = None) -> Any — POST /v1/candidates/add — add candidates, optionally into a campaign/projects.
  • dashboard_statistics(date_from: str, date_to: str) -> Any — GET /v1/statistics/dashboard — both dates required, format YYYY-MM-DD.
  • list_campaigns(user_email: str | None = None, include_metrics: bool | None = None, partner: str | None = None, partner_filters: dict | None = None) -> Any — GET /v1/campaigns/list. Returns campaigns created by userEmail (if set).
  • list_projects(user_email: str | None = None) -> Any — GET /v1/projects/list — list projects (for the user if userEmail set).
  • modify_candidate(candidate: dict, user_email: str | None = None) -> Any — POST /v1/candidates/modify — update a candidate. candidate must include candidateId.
  • search_candidates(key: str, value: str, user_email: str | None = None) -> Any — GET /v1/candidates/search — find candidates by an identifier.
  • zapier_subscribe(url: str, subscription_type: str) -> Any — POST /v1/zapier/subscribe — register a webhook URL for a subscription type.
  • zapier_unsubscribe(subscription_id: str) -> Any — POST /v1/zapier/unsubscribe — remove a webhook subscription by id.