---
name: needle
description: |
  Needle is an outbound engine for AI agents: leads, campaigns, LinkedIn,
  Instagram, places, suppressions, and compose. One API key unlocks the REST
  API and the MCP server. Use when an agent needs to source leads, run
  outreach, integrate Needle into an app, or obtain an API key.
---

# Needle

Needle helps agents search people and places, build lead lists, run multi-channel campaigns on LinkedIn and Instagram, keep suppressions, preview messages, and send from connected accounts.

The same outbound engine is available as a REST API and as MCP tools. One API key unlocks both. Listing MCP tools does not require a key; calling tools and REST endpoints does.

## Get started

No install. Get an API key, then pick a path.

- **Dashboard / API key**: https://needle.app/api-keys
- **Sign up**: https://needle.app/login
- **MCP**: `undefined/mcp/v1`
- **REST**: `undefined/api/v1`
- **OpenAPI**: https://needle.app/api/v1/openapi
- **Docs**: https://needle.app/docs/introduction
- **Full index**: https://needle.app/llms-full.txt

This skill is the routing layer. After you know the path, use the API Reference or MCP catalog for schemas.

## Get credentials

Needle users create an API key in the dashboard:

- Open https://needle.app/api-keys
- Create a key. It looks like `sk-ndl-…`.
- Each key is bound to one user and one organization.

If they do not have an account yet, send them to Path D.

Sending, following, and inviting require a connected LinkedIn or Instagram account. Connecting an account is dashboard-only at `/accounts`. The API and MCP can list, update, and delete connected accounts; they cannot start the OAuth connect flow.

**Already have `NEEDLE_API_KEY`?** Skip credential setup. Pick a path below.

## Choose your path

All paths use the same API key. The difference is what you do next.

- **Need outbound during this session** → Path A (MCP tools)
- **Need to add Needle to app code** → Path B (REST in the product)
- **Need a finished outbound workflow** → Path C (source, campaign, or one-off send)
- **Need more than one of the above** → do them in sequence; one key covers everything
- **Need an account or API key** → Path D
- **Do not want to connect MCP** → Path E (REST directly)

| Path | Question it answers | Where the work runs |
| --- | --- | --- |
| A | Which Needle tool should I call right now? | In the agent session, over MCP |
| B | How do I add a Needle API call to this codebase? | Inside the user's product code |
| C | What is the finished outbound workflow? | In the agent session, producing a result |
| D | How does the human get a key? | Dashboard |
| E | How do I call Needle over HTTP? | REST at `/api/v1` |

## Path A: Live MCP tools

Use this when you need outbound during your work: searching people, building leads, running campaigns, sending LinkedIn or Instagram messages, or checking suppressions.

Connect at `undefined/mcp/v1` with `Authorization: Bearer sk-ndl-…`. Listing tools at `https://needle.app/mcp/v1/tools` does not require a key; calling them does.

```json
{
  "mcpServers": {
    "needle": {
      "url": "undefined/mcp/v1",
      "headers": {
        "Authorization": "Bearer sk-ndl-…"
      }
    }
  }
}
```

Works with Claude Code, Cowork, Codex, Cursor, Hermes, or any MCP client.

Route by job:

- **Who am I bound to?** → `needleGetMe`
- **What can I send from?** → `needleListConnectedAccounts`
- **Find people** → `needleSearchLinkedinPeople`, then `needleGetLinkedinProfile`
- **Find companies / locations** → `needleSearchPlaces`, then `needleGetPlace`
- **Build a list** → `needleCreateLead` / `needleListLeads`
- **Do not contact** → `needleCheckSuppressions` / `needleCreateSuppressions`
- **Preview copy** → `needleComposePreview` before any send
- **One-off LinkedIn** → `needleSendLinkedinInvitation` or `needleSendLinkedinMessage`
- **One-off Instagram** → `needleFollowInstagramUser` or `needleSendInstagramMessage`
- **Campaign** → `needleCreateCampaign`, `needleEnrollCampaignLeads`, `needleListPendingApprovals`, `needleApprovePendingTask`

Default flow for live outbound:

1. `needleGetMe` to confirm the user and organization.
2. `needleListConnectedAccounts` before any send. If none are connected, send the human to `/accounts`.
3. Search people or places, then create leads.
4. `needleCheckSuppressions` before outreach.
5. `needleComposePreview` when the message is generated.
6. Enroll in a campaign, or send a one-off invitation/message.
7. Watch `needleGetCampaignStats` / `needleSearchEvents` after a campaign is live.

If the task becomes "wire Needle into product code," switch to Path B.

### MCP tools

#### Me

- [`needleGetMe`](/docs/mcp/needle-get-me): Get the authenticated user

#### Connected accounts

- [`needleListConnectedAccounts`](/docs/mcp/needle-list-connected-accounts): List connected accounts
- [`needleGetConnectedAccount`](/docs/mcp/needle-get-connected-account): Get a connected account
- [`needleUpdateConnectedAccount`](/docs/mcp/needle-update-connected-account): Update a connected account
- [`needleDeleteConnectedAccount`](/docs/mcp/needle-delete-connected-account): Delete a connected account
- [`needleListConnectedAccountEvents`](/docs/mcp/needle-list-connected-account-events): List connected account events

#### Leads

- [`needleListLeads`](/docs/mcp/needle-list-leads): List leads
- [`needleCreateLead`](/docs/mcp/needle-create-lead): Create a lead
- [`needleGetLead`](/docs/mcp/needle-get-lead): Get a lead
- [`needleUpdateLead`](/docs/mcp/needle-update-lead): Update a lead
- [`needleDeleteLead`](/docs/mcp/needle-delete-lead): Delete a lead
- [`needleListLeadEvents`](/docs/mcp/needle-list-lead-events): List lead events

#### Campaigns

- [`needleListCampaigns`](/docs/mcp/needle-list-campaigns): List campaigns
- [`needleCreateCampaign`](/docs/mcp/needle-create-campaign): Create a campaign
- [`needleGetCampaign`](/docs/mcp/needle-get-campaign): Get a campaign
- [`needleUpdateCampaign`](/docs/mcp/needle-update-campaign): Update a campaign
- [`needleArchiveCampaign`](/docs/mcp/needle-archive-campaign): Archive a campaign
- [`needleGetCampaignStats`](/docs/mcp/needle-get-campaign-stats): Get campaign stats
- [`needleListCampaignEvents`](/docs/mcp/needle-list-campaign-events): List campaign events
- [`needleListCampaignLeads`](/docs/mcp/needle-list-campaign-leads): List campaign leads
- [`needleEnrollCampaignLeads`](/docs/mcp/needle-enroll-campaign-leads): Enroll leads
- [`needleRemoveCampaignLead`](/docs/mcp/needle-remove-campaign-lead): Stop a lead in a campaign
- [`needleListPendingApprovals`](/docs/mcp/needle-list-pending-approvals): List pending approvals
- [`needleApprovePendingTask`](/docs/mcp/needle-approve-pending-task): Approve a pending send

#### Events

- [`needleSearchEvents`](/docs/mcp/needle-search-events): Search events

#### LinkedIn

- [`needleGetLinkedinProfile`](/docs/mcp/needle-get-linkedin-profile): Get a LinkedIn profile
- [`needleSearchLinkedinPeople`](/docs/mcp/needle-search-linkedin-people): Search LinkedIn people
- [`needleSendLinkedinMessage`](/docs/mcp/needle-send-linkedin-message): Send a LinkedIn message
- [`needleListLinkedinConversationMessages`](/docs/mcp/needle-list-linkedin-conversation-messages): List messages in a conversation
- [`needleListLinkedinInvitations`](/docs/mcp/needle-list-linkedin-invitations): List pending invitations
- [`needleSendLinkedinInvitation`](/docs/mcp/needle-send-linkedin-invitation): Send a connection invitation
- [`needleWithdrawLinkedinInvitation`](/docs/mcp/needle-withdraw-linkedin-invitation): Withdraw an invitation
- [`needleListLinkedinRelations`](/docs/mcp/needle-list-linkedin-relations): List first-degree connections

#### Instagram

- [`needleGetInstagramProfile`](/docs/mcp/needle-get-instagram-profile): Get an Instagram profile
- [`needleSendInstagramMessage`](/docs/mcp/needle-send-instagram-message): Send an Instagram message
- [`needleListInstagramConversationMessages`](/docs/mcp/needle-list-instagram-conversation-messages): List messages in a conversation
- [`needleFollowInstagramUser`](/docs/mcp/needle-follow-instagram-user): Follow an Instagram user
- [`needleUnfollowInstagramUser`](/docs/mcp/needle-unfollow-instagram-user): Unfollow an Instagram user

#### Places

- [`needleSearchPlaces`](/docs/mcp/needle-search-places): Search places
- [`needleGetPlace`](/docs/mcp/needle-get-place): Get a place

#### Compose

- [`needleComposePreview`](/docs/mcp/needle-compose-preview): Preview an AI-composed message

#### Suppressions

- [`needleListSuppressions`](/docs/mcp/needle-list-suppressions): List suppressions
- [`needleCreateSuppressions`](/docs/mcp/needle-create-suppressions): Create suppressions
- [`needleCheckSuppressions`](/docs/mcp/needle-check-suppressions): Check people and companies against the suppression list
- [`needleDeleteSuppression`](/docs/mcp/needle-delete-suppression): Delete a suppression

## Path B: Integrate Needle into an app

Use this when you are building an application, agent, or workflow that calls Needle **from code**, meaning the integration will run inside the user's product rather than from this session's MCP client.

This is the key difference from Path A: Path A calls MCP tools during the current session. Path B writes code that keeps running after the agent stops, using `NEEDLE_API_KEY` from the project's `.env` or runtime config.

```dotenv
NEEDLE_API_KEY=sk-ndl-…
```

**Base URL:** `undefined/api/v1`

**Auth header:** `Authorization: Bearer sk-ndl-…`

- **OpenAPI:** https://needle.app/api/v1/openapi
- **API Reference:** https://needle.app/docs/api-reference/introduction

Smoke-test with `GET /api/v1/me` before writing feature code:

```bash
curl undefined/api/v1/me \
  -H "Authorization: Bearer sk-ndl-…" \
  -H "Content-Type: application/json"
```

Route the feature the same way as Path A, then call the matching REST endpoint from Path E. Sends still need a connected account in the organization that owns the key.

If you do not have a key yet, do Path D first.

## Path C: Repeatable outbound workflows

Use this when the goal is a finished outbound result (a lead list, a live campaign, a one-off send, or a suppression pass), not raw tool output and not product-code integration.

### Source a list

1. `needleSearchLinkedinPeople` (keywords or a LinkedIn / Sales Navigator `searchUrl`) or `needleSearchPlaces`.
2. `needleGetLinkedinProfile` / `needleGetPlace` when you need details.
3. `needleCreateLead` for each person you will contact. A lead can carry LinkedIn and/or Instagram identities.
4. `needleCheckSuppressions` and drop anyone you should not contact.

### Run a campaign

1. Confirm at least one connected account (`needleListConnectedAccounts`).
2. `needleCreateCampaign` with the sequence graph, schedule, limits, and the connected accounts to send from.
3. `needleEnrollCampaignLeads`.
4. `needleListPendingApprovals` and `needleApprovePendingTask` when a send is waiting.
5. `needleGetCampaignStats` and `needleSearchEvents` to see what happened.

### One-off outreach

1. Get the profile.
2. `needleComposePreview` for the message.
3. LinkedIn: `needleSendLinkedinInvitation` or `needleSendLinkedinMessage`.
4. Instagram: `needleFollowInstagramUser` or `needleSendInstagramMessage`.

### Keep people off the list

`needleCreateSuppressions` for people or companies that should not be contacted. Campaigns honor the suppression policy you set on create/update.

If the request shifts to "wire Needle into product code," switch to Path B. If you only need a single tool call, stay on Path A.

## Path D: Account or API key

Use this when the human still needs to sign up, sign in, or create a key.

1. If they have no account, open https://needle.app/login and sign up.
2. Create a key at https://needle.app/api-keys. Save it as `NEEDLE_API_KEY`.
3. To send, connect LinkedIn and/or Instagram at https://needle.app/accounts.
4. Continue with Path A, B, C, or E.

There is no keyless tier. Listing MCP tools at `/mcp/v1/tools` is public; every other call needs a key.

## Path E: REST API without MCP

Use this when you do not want to connect an MCP client. This works for both live work in this session and app integrations (Path B).

**Base URL:** `undefined/api/v1`

**Auth header:** `Authorization: Bearer sk-ndl-…`

```bash
curl undefined/api/v1/me \
  -H "Authorization: Bearer sk-ndl-…" \
  -H "Content-Type: application/json"
```

Successful requests return 2xx. `401` means a missing or invalid key. `422` means an invalid payload.

### REST endpoints

#### Me

- `GET /api/v1/me`: [Get the authenticated user](/docs/api-reference/get-me)

#### Connected accounts

- `GET /api/v1/connected-accounts`: [List connected accounts](/docs/api-reference/list-connected-accounts)
- `GET /api/v1/connected-accounts/{connectedAccountId}`: [Get a connected account](/docs/api-reference/get-connected-account)
- `PATCH /api/v1/connected-accounts/{connectedAccountId}`: [Update a connected account](/docs/api-reference/update-connected-account)
- `DELETE /api/v1/connected-accounts/{connectedAccountId}`: [Delete a connected account](/docs/api-reference/delete-connected-account)
- `GET /api/v1/connected-accounts/{connectedAccountId}/events`: [List connected account events](/docs/api-reference/list-connected-account-events)

#### Leads

- `GET /api/v1/leads`: [List leads](/docs/api-reference/list-leads)
- `POST /api/v1/leads`: [Create a lead](/docs/api-reference/create-lead)
- `GET /api/v1/leads/{leadId}`: [Get a lead](/docs/api-reference/get-lead)
- `PATCH /api/v1/leads/{leadId}`: [Update a lead](/docs/api-reference/update-lead)
- `DELETE /api/v1/leads/{leadId}`: [Delete a lead](/docs/api-reference/delete-lead)
- `GET /api/v1/leads/{leadId}/events`: [List lead events](/docs/api-reference/list-lead-events)

#### Campaigns

- `GET /api/v1/campaigns`: [List campaigns](/docs/api-reference/list-campaigns)
- `POST /api/v1/campaigns`: [Create a campaign](/docs/api-reference/create-campaign)
- `GET /api/v1/campaigns/{campaignId}`: [Get a campaign](/docs/api-reference/get-campaign)
- `PATCH /api/v1/campaigns/{campaignId}`: [Update a campaign](/docs/api-reference/update-campaign)
- `DELETE /api/v1/campaigns/{campaignId}`: [Archive a campaign](/docs/api-reference/archive-campaign)
- `GET /api/v1/campaigns/{campaignId}/stats`: [Get campaign stats](/docs/api-reference/get-campaign-stats)
- `GET /api/v1/campaigns/{campaignId}/events`: [List campaign events](/docs/api-reference/list-campaign-events)
- `GET /api/v1/campaigns/{campaignId}/leads`: [List campaign leads](/docs/api-reference/list-campaign-leads)
- `POST /api/v1/campaigns/{campaignId}/leads`: [Enroll leads](/docs/api-reference/enroll-campaign-leads)
- `DELETE /api/v1/campaigns/{campaignId}/leads/{leadId}`: [Stop a lead in a campaign](/docs/api-reference/remove-campaign-lead)
- `GET /api/v1/campaigns/{campaignId}/pending`: [List pending approvals](/docs/api-reference/list-pending-approvals)
- `PATCH /api/v1/campaigns/{campaignId}/pending/{taskId}`: [Approve a pending send](/docs/api-reference/approve-pending-task)

#### Events

- `POST /api/v1/events/search`: [Search events](/docs/api-reference/search-events)

#### LinkedIn

- `GET /api/v1/linkedin/profiles/{identity}`: [Get a LinkedIn profile](/docs/api-reference/get-linkedin-profile)
- `POST /api/v1/linkedin/search`: [Search LinkedIn people](/docs/api-reference/search-linkedin-people)
- `POST /api/v1/linkedin/messages`: [Send a LinkedIn message](/docs/api-reference/send-linkedin-message)
- `GET /api/v1/linkedin/conversations/{conversationId}/messages`: [List messages in a conversation](/docs/api-reference/list-linkedin-conversation-messages)
- `GET /api/v1/linkedin/invitations`: [List pending invitations](/docs/api-reference/list-linkedin-invitations)
- `POST /api/v1/linkedin/invitations`: [Send a connection invitation](/docs/api-reference/send-linkedin-invitation)
- `DELETE /api/v1/linkedin/invitations/{invitationId}`: [Withdraw an invitation](/docs/api-reference/withdraw-linkedin-invitation)
- `GET /api/v1/linkedin/relations`: [List first-degree connections](/docs/api-reference/list-linkedin-relations)

#### Instagram

- `GET /api/v1/instagram/profiles/{identity}`: [Get an Instagram profile](/docs/api-reference/get-instagram-profile)
- `POST /api/v1/instagram/messages`: [Send an Instagram message](/docs/api-reference/send-instagram-message)
- `GET /api/v1/instagram/conversations/{conversationId}/messages`: [List messages in a conversation](/docs/api-reference/list-instagram-conversation-messages)
- `POST /api/v1/instagram/follows`: [Follow an Instagram user](/docs/api-reference/follow-instagram-user)
- `DELETE /api/v1/instagram/follows/{identity}`: [Unfollow an Instagram user](/docs/api-reference/unfollow-instagram-user)

#### Places

- `POST /api/v1/places/search`: [Search places](/docs/api-reference/search-places)
- `GET /api/v1/places/{placeId}`: [Get a place](/docs/api-reference/get-place)

#### Compose

- `POST /api/v1/compose`: [Preview an AI-composed message](/docs/api-reference/compose-preview)

#### Suppressions

- `GET /api/v1/suppressions`: [List suppressions](/docs/api-reference/list-suppressions)
- `POST /api/v1/suppressions`: [Create suppressions](/docs/api-reference/create-suppressions)
- `POST /api/v1/suppressions/check`: [Check people and companies against the suppression list](/docs/api-reference/check-suppressions)
- `DELETE /api/v1/suppressions/{suppressionId}`: [Delete a suppression](/docs/api-reference/delete-suppression)

## References

- **This skill:** https://needle.app/agent-onboarding/SKILL.md
- **Documentation:** https://needle.app/docs/introduction
- **API Reference:** https://needle.app/docs/api-reference/introduction
- **MCP catalog:** https://needle.app/docs/mcp/introduction
- **OpenAPI:** https://needle.app/api/v1/openapi
- **Full index:** https://needle.app/llms-full.txt
