Integration API Documentation

API Reference for integrating with Draftboard platform | Version 0.9.17

Authentication

Bearer Token Authentication

All API requests require authentication using a Bearer token. Include your API token in the Authorization header of every request.

Generate and manage your API tokens in the app under Settings → API Keys.

If you have any questions, contact [email protected]

Authorization Header
Authorization: Bearer YOUR_API_TOKEN_HERE

Note: Keep your API token secure. Do not share it publicly or commit it to version control.

Base URL

https://intros.draftboard.com

Changelog

Changes are grouped by release, then by type — New (additions), Changed (behavior changes to existing endpoints), and Removed (deletions). The most recent release is at the top.

0.9.17

Filtering targets by a tag you do not have is refused instead of ignored.

Changed

0.9.16

See how many of your connections carry each label before you filter. One new read endpoint, and clearer refusals.

New Changed

0.9.15

Importing supporters no longer rates them for you. The request and response are unchanged.

Changed

0.9.14

Find people by label on more endpoints, see the labels on every connection, and look up the company to label. Additive except one: an account id is no longer accepted where a company id is expected. Requests without the new filters return the same results as before.

New Changed

0.9.13

Ask for paths only through the people you would actually ask. Additive only: a new optional filter on two endpoints and your own star rating on each connection. Requests without the filter return the same results as before.

New

0.9.12

GET/me reports your credit balance — the number your writes depend on. Two fields that described limits no longer in force are removed.

New Changed Removed

0.9.11

List your own LinkedIn connections, or a teammate's, and see who on your team knows each person. Additive only: a new optional filter and a new owners field on supporters. Requests without the filter return the same list as before.

New Changed

0.9.10

Labels: say who a person is to you, then work only through that group — for example "paths to this target through our investors". Additive only: two new endpoints, new optional filters, and a new labels field on supporters. Existing requests and response fields are unchanged.

New

0.9.9

A supporter search can score everyone it finds against your ideal customer. Additive only: the new request inputs (icp, minIcpFit) are optional, and icpFits is a new response field on pool prospects. Existing requests and response fields are unchanged.

New

0.9.8

A supporter search now leaves every person it finds in the pool for you to review.

Changed

0.9.7

Documentation only — no endpoint, field or behavior changed.

Changed

0.9.6

Your strongest open paths, in one call.

New

0.9.5

Find a saved target by name.

New

0.9.4

Tag filters can now match any of the tags you pass. Several tags used to mean "carries every one"; that is still the default.

New

0.9.3

The two search endpoints no longer cap how much you can send them. POST /search/accounts and POST /search/supporters refused a request that carried more than a set number of companies, titles or supporters. They now take as many as you have.

Changed

0.9.2

The out-of-credits refusal now answers in the same shape as every other error. A 402 used to arrive in a body of its own, so a client that parsed our errors correctly everywhere else failed to parse the one that says the balance ran out.

Changed

0.9.1

Documentation only — no endpoint, field or behavior changed. This page described two gates that do not exist and named the wrong codes for the ones that do. Everything below is a correction to the reference, not a change to the API.

Changed

0.9.0

The companies you cannot reach, and the strength behind the order. GET/accounts can now list the companies where you have saved people and hold no path at all, returns the rank it sorts by, and no longer shrinks a company's counts when your search matched a person.

New Changed

0.8.1

Documentation only — no endpoint, field or behavior changed.

Changed

0.8.0

Structured relationships, and a rating that reads the right way up. Sort connections by how the connector knows the target without parsing our sentences, and read or write a connector's star rating in the same direction the app shows it.

New Changed

0.7.0

Connector ratings (tiers). Set and read a personal "star rating" for any connector, straight from the API.

New Changed

0.6.0

Supporter Search. Scan the networks of specific supporters (people you know) for people matching a persona, straight from the API.

New

0.5.0

Account Search. Kick off a search for people in specific roles at specific companies, straight from the API.

New

0.4.0

Filter targets by title. Narrow your saved targets to a job title / position with a single query parameter.

New

0.3.0

Team-shared Shortlist. Pin the most promising intros (paths), hand them off between teammates with a note, and move them through their lifecycle — all over the API.

New Changed

0.2.0

New

0.1.0

New

New endpoints:

New fields:

Changed Removed

Get Current User

GET /api/v1/integration/me

Returns information about the authenticated customer: their profile, their team, their credit balance, and their subscription if one is in force. Writes are paid from credits; when it reaches 0 they are refused with 402 INSUFFICIENT_CREDITS.

Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/me' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetMeResponseDto
{
  "status": 200,
  "errors": [],
  "customer": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Acme Corporation",
    "user": {
      "id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
      "firstName": "John",
      "lastName": "Smith",
      "linkedinUrl": "https://www.linkedin.com/in/johnsmith/"
    },
    "subscription": {
      "planCode": "team",
      "planName": "Team Plan",
      "seatLimit": 3,
      "seatUsed": 2,
      "copilotEnabled": true
    },
    "credits": 148,
    "customerProfileId": "c0ffee00-1111-2222-3333-444455556666",
    "teamMembers": [
      {
        "id": "c0ffee00-1111-2222-3333-444455556666",
        "firstName": "John",
        "lastName": "Smith",
        "linkedinUrl": "https://www.linkedin.com/in/johnsmith/"
      },
      {
        "id": "d1d2d3d4-5566-7788-99aa-bbccddeeff00",
        "firstName": "Sue",
        "lastName": "Jones",
        "linkedinUrl": "https://www.linkedin.com/in/suejones/"
      }
    ]
  }
}

Response DTO: IntegrationCustomerDto

Field Type Description
id string Customer ID
name string Customer/Company name
user IntegrationUserDto API key owner info
subscription IntegrationSubscriptionDto Your subscription: planCode, planName, seatLimit, seatUsed, copilotEnabled. Absent unless you have a subscription in force — none, or one that has expired.
credits number Your organization's credit balance, in credits — the balance writes are paid from, shared by everyone on your team. At 0, writes are refused with 402 INSUFFICIENT_CREDITS. Absent when the balance could not be read: treat that as unknown, not as 0.
customerProfileId string Your own member id — the API key owner's. Pass it as filters[ownerIds][] on GET/supporters to list your own LinkedIn connections, and — like any teamMembers[].id — as assigneeCustomerProfileId for shortlist assignment.
teamMembers TeamMemberDto[] Active members of the caller's organization, you included — the assignee roster, and the ids to pass as filters[ownerIds][] on GET/supporters.

Nested DTO: IntegrationUserDto

Field Type Description
id string User ID (owner_id)
firstName string First name
lastName string Last name
linkedinUrl string LinkedIn profile URL (optional)

Nested DTO: TeamMemberDto

Field Type Description
id string Team member customer_profile_id — use as assigneeCustomerProfileId for shortlist assignment
firstName string First name
lastName string Last name
linkedinUrl string LinkedIn profile URL (optional)

List Tags

GET /api/v1/integration/tags

Returns a paginated list of tags associated with the authenticated customer. Tags can be filtered by name or type.

Query Parameters

Parameter Type Description
query string Search by tag name
type string Filter by tag type: manual or automatic
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page. Default: 20
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/tags?pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetTagsResponseDto
{
  "status": 200,
  "errors": [],
  "tags": [
    {
      "id": "62e5d269-ab59-4052-bd67-7eeb7050e626",
      "title": "28-Jan-2026",
      "type": "automatic"
    },
    {
      "id": "e2a94bfa-2942-41b8-9ba7-f6b1274380d8",
      "title": "CEO",
      "type": "manual"
    },
    {
      "id": "d453a772-39c9-4c83-9247-0e5c0ea3c529",
      "title": "Performance Marketing",
      "type": "manual"
    }
  ],
  "count": 9,
  "nextPage": 0
}

Response DTO: IntegrationTagDto

Field Type Description
id string Unique identifier
title string Tag title/name
type string Tag type: automatic or manual

List Targets

GET /api/v1/integration/targets

Returns a paginated list of targets (prospects) with their profiles, status, positions, and connection paths information.

Query Parameters

Parameter Type Description
updatedSince string Return targets updated after this timestamp (ISO 8601 format)
tagIds array[string] Filter by tag IDs. An id that is not one of your tags is refused with 400.
tagNames array[string] Filter by tag names, case-insensitive. A name that is not one of your tags is refused with 400 naming it — take names from GET/tags.
tagMatch string How several tags combine: any returns targets carrying at least one of them, all only those carrying every one. Default all.
statuses array[string] Filter by status. Available values:
new completed stopped
accountId string Filter to targets at a single account (company) by id — e.g. the id returned by GET /accounts. Drill from a company to its targets.
title string Filter targets by title / position (case-insensitive substring; max 200 chars). e.g. ?title=Head%20of%20Sales. To narrow by company, resolve the account via GET /accounts then use accountId.
name string Find targets by name: every word must appear in the target's first or last name, in any order, case-insensitive (max 200 chars). e.g. ?name=Jim%20Smith. Like the other filters it only narrows this list, so a target this list does not show is not found by name either — to look one person up by their LinkedIn profile, use GET /targets/resolve.
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page. Default: 20
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/targets?pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetTargetsResponseDto
{
  "status": 200,
  "errors": [],
  "targets": [
    {
      "id": "6f397389-dc11-4051-822a-5ffd25ec8417",
      "firstName": "Michael",
      "lastName": "Johnson",
      "linkedinUrl": "https://www.linkedin.com/in/michaeljohnson/",
      "status": "new",
      "position": {
        "title": "Chief Executive Officer",
        "companyName": "Tech Corp",
        "companyLinkedinUrl": "https://www.linkedin.com/company/techcorp/"
      },
      "headline": "CEO at Tech Corp | Building the future",
      "score": 74,
      "connectionsNumber": 68,
      "degree": "1st",
      "tags": ["Q1-2026", "VIP"],
      "createdAt": "2026-01-13T09:24:58.468Z",
      "updatedAt": "2026-01-13T09:25:02.612Z",
      "avatarUrl": "https://files-intros.draftboard.com/p/profiles/abc123/avatar/abc123.png"
    },
    {
      "id": "bde92534-d194-462c-adee-6248bd2da55a",
      "firstName": "Sarah",
      "lastName": "Williams",
      "linkedinUrl": "https://www.linkedin.com/in/sarahwilliams/",
      "status": "completed",
      "position": {
        "title": "VP of Marketing",
        "companyName": "Growth Inc"
      },
      "headline": "VP Marketing | Growth Expert",
      "score": 45,
      "connectionsNumber": 16,
      "degree": "2nd",
      "tags": ["Onboarding"],
      "createdAt": "2026-01-12T12:40:12.456Z",
      "updatedAt": "2026-01-12T12:40:13.994Z"
    }
  ],
  "count": 3,
  "nextPage": 0
}

Response DTO: IntegrationTargetDto

Field Type Description
id string Unique identifier
firstName string First name
lastName string Last name
linkedinUrl string LinkedIn profile URL (optional)
status string Current status: new, processing, ready, in_progress, completed, no_paths, error, or stopped
position IntegrationPositionDto Current position
headline string LinkedIn headline (optional)
score number Relationship score (0-100)
connectionsNumber number Number of available connections
degree string Degree of separation to this target: "1st" (you or a teammate are directly connected) or "2nd" (reachable via one connector). Absent when no connection path exists.
tags string[] Assigned tag names
createdAt string Creation timestamp (ISO 8601)
updatedAt string Last update timestamp (ISO 8601)
avatarUrl string Absolute URL of the avatar image (LinkedIn-sourced via filestore), e.g. https://files-intros.draftboard.com/p/.... Omitted entirely when there is no stored avatar.

Nested DTO: IntegrationPositionDto

Field Type Description
title string Job title
companyName string Company name
companyLinkedinUrl string Company LinkedIn URL (optional)

Resolve Target by LinkedIn URL

GET /api/v1/integration/targets/resolve

Resolves a LinkedIn profile URL to a single one of your targets — the intermediary step for going from a LinkedIn URL to a target_id you can then pass to the ID-scoped endpoints (e.g. GET /targets/{target_id}/connections, POST /targets/{target_id}/tags, DELETE /targets/{target_id}). Returns the full IntegrationTargetDto (same shape as items in GET /targets). Read-only.

Identity, not list visibility. Unlike GET /targets (which lists only targets that already have a warm-intro path), this endpoint resolves any of your non-archived targets — so the id is available even for a freshly imported, not-yet-indexed target (its score will be 0 and degree absent until paths are found). Matching is on the LinkedIn vanity slug: case-insensitive and tolerant of www, trailing slashes, and query strings.

Query Parameters

Parameter Type Description
linkedinUrl string Required. The target's LinkedIn profile URL, e.g. https://www.linkedin.com/in/janedoe/. Must be a profile (/in/…) URL — a non-profile or malformed value returns 400.
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/targets/resolve?linkedinUrl=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fjanedoe%2F' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: ResolveTargetResponseDto
{
  "status": 200,
  "errors": [],
  "target": {
    "id": "6f397389-dc11-4051-822a-5ffd25ec8417",
    "firstName": "Jane",
    "lastName": "Doe",
    "linkedinUrl": "https://www.linkedin.com/in/janedoe/",
    "status": "new",
    "position": {
      "title": "Chief Executive Officer",
      "companyName": "Tech Corp",
      "companyLinkedinUrl": "https://www.linkedin.com/company/techcorp/"
    },
    "headline": "CEO at Tech Corp | Building the future",
    "score": 74,
    "connectionsNumber": 68,
    "degree": "1st",
    "tags": ["Q1-2026", "VIP"],
    "createdAt": "2026-01-13T09:24:58.468Z",
    "updatedAt": "2026-01-13T09:25:02.612Z",
    "avatarUrl": "https://files-intros.draftboard.com/p/profiles/abc123/avatar/abc123.png"
  }
}

The target object is the full IntegrationTargetDto — see the field reference under GET /targets.

Not found. When none of your non-archived targets match the URL, the endpoint returns HTTP 404 (the target field is not present). A 400 is returned when linkedinUrl is missing or is not a LinkedIn profile URL.

Import Targets

POST /api/v1/integration/targets/import

Import new targets by providing their LinkedIn URLs. Optionally assign tags to the imported targets.

Accepted, not finished — and how to confirm it. A 200 with imported means the batch was accepted. The targets are created shortly afterwards and their warm-intro paths are computed after that; both are asynchronous and can take several minutes.

To confirm a specific person landed, call GET /targets/resolve?linkedinUrl=… with the URL you imported. It returns the target as soon as the row exists — before any path is found — so a 200 confirms the write and hands you the target_id.

Do not confirm an import with GET /targets (filtered by tags, updatedSince or anything else): that endpoint lists only targets that already have a warm-intro path, so a freshly imported person is legitimately absent from it until paths are found. An empty result there is not evidence the import failed. Once resolve returns the id, poll GET /targets/{target_id}/connections for the paths as they appear.

Request Body

Field Type Description
linkedinUrls required array[string] LinkedIn profile URLs to import. 1–1000 entries per request (a larger batch returns 400).
tags array[string] Tag names to assign to imported targets (auto-created if not present). Max 100 chars each; max 50 entries.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/targets/import' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "linkedinUrls": [
      "https://www.linkedin.com/in/example-profile-1/",
      "https://www.linkedin.com/in/example-profile-2/"
    ],
    "tags": [
      "Q1-2026",
      "VIP"
    ]
  }'
Response: ImportTargetsResponseDto
{
  "status": 200,
  "errors": [],
  "imported": 2,
  "notImported": 0,
  "failedUrls": []
}
Response with Errors
{
  "status": 200,
  "errors": ["Invalid LinkedIn URL format"],
  "imported": 1,
  "notImported": 1,
  "failedUrls": ["invalid-url-format"]
}

Request DTO: ImportTargetsRequestDto

Field Type Description
linkedinUrls required string[] List of LinkedIn profile URLs to import. 1–1000 entries per request.
tags string[] Tag names to apply (will be created if not exist)

Response DTO: ImportTargetsResponseDto

Field Type Description
status number Status code
errors string[] Error messages
imported number Number of successfully imported targets
notImported number Number of targets that failed to import
failedUrls string[] LinkedIn URLs that failed to import

Get Target Connections

GET /api/v1/integration/targets/{target_id}/connections

Returns a paginated list of connections (paths) to a specific target. Each connection includes the connector's profile, their relationship rank with the target, and details about their shared history — as prose in scoreDetails and, where we have it, as data in relationships and relationshipDetails.

The structured relationship fields are present when we hold that signal for the pair, and omitted when we do not. Read them defensively — connection.relationships ?? [] — and treat an absent field as "we have no structured signal for this pair": it is not a statement about the strength of the connection, and it does not mean the two people have no relationship. scoreDetails is unchanged and carries the human-readable summary; keep reading it.

Empty means absent, not []. Exactly like scoreDetails today, an empty list is omitted from the JSON entirely rather than serialized as an empty array. Read them defensively — connection.relationships ?? [] — and do not treat a missing key as an error.

The two fields are independent. Neither is derived from the other, and their positions do not line up: one relationships token can sit next to several relationshipDetails records, or none. A connection whose only structured signal is shared contacts produces a relationshipDetails record (mutualConnections) and no relationships entry at all, because "mutual connections" is not one of the three relationship types. Conversely a rank can carry relationships with no relationshipDetails. Never index-align either field against the other, or against scoreDetails.

Path Parameters

Parameter Type Description
target_id required string UUID of the target

Query Parameters

Parameter Type Description
updatedSince string Return connections updated after this timestamp (ISO 8601 format)
ownerIds array[string] Filter by owner IDs (team member IDs)
connectorLabels array[string] Only paths through a connector carrying these labels, e.g. connectorLabels=investor — "paths to this target through our investors". Same tokens as labels on GET/supporters; an unknown token is a 400.
connectorLabelsMatch string How several connectorLabels combine: all (default) or any.
excludeConnectorLabels array[string] Paths through a connector carrying any of these labels are left out, e.g. excludeConnectorLabels=do_not_contact.
rating array[number] Only paths through connectors you rated with one of these stars, 1..5 where higher is better (5 = "ask anytime"). Repeat it or comma-join it: rating=4&rating=5 = "paths through people I would actually ask". filters[rating][]=4&filters[rating][]=5, the spelling GET/supporters uses, means the same. Your own rating, never a teammate's. Connectors you have not rated are left out while the filter is set. 1 ("do not ask") also returns the connectors you hid.
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page. Default: 20
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/targets/6f397389-dc11-4051-822a-5ffd25ec8417/connections?pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — paths to this target through your investors
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/targets/6f397389-dc11-4051-822a-5ffd25ec8417/connections?connectorLabels=investor&excludeConnectorLabels=do_not_contact' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — paths to this target through people you rated 4 or 5 stars
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/targets/6f397389-dc11-4051-822a-5ffd25ec8417/connections?rating=4&rating=5' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetConnectionsResponseDto
{
  "status": 200,
  "errors": [],
  "connections": [
    {
      "id": "daf886d4-d2ca-4ca2-8cd7-5546c50ada5f",
      "firstName": "David",
      "lastName": "Chen",
      "linkedinUrl": "https://www.linkedin.com/in/davidchen/",
      "position": {
        "title": "Director of Engineering",
        "companyName": "Draftboard",
        "companyLinkedinUrl": "https://www.linkedin.com/company/draftboard/"
      },
      "headline": "Engineering Leader | Building great teams",
      "score": 48,
      "scoreDetails": [
        "They overlapped for a long time (42 months) @ TechCorp, most recently in 2016"
      ],
      "relationships": [
        "former_colleague"
      ],
      "relationshipDetails": [
        {
          "employment": {
            "company": "TechCorp",
            "department": "Engineering",
            "overlapStartDate": "2012-09-01",
            "overlapEndDate": "2016-03-01",
            "loose": false
          },
          "score": 0.42
        },
        {
          "mutualConnections": {
            "count": 17
          },
          "score": 0.08
        }
      ],
      "owners": [
        {
          "id": "0dda7490-eea3-4c1c-8cec-a4824222f116",
          "firstName": "John",
          "lastName": "Smith",
          "linkedinUrl": "https://www.linkedin.com/in/johnsmith/",
          "score": 85
        }
      ],
      "avatarUrl": "https://files-intros.draftboard.com/p/profiles/abc123/avatar/abc123.png",
      "rating": 5
    },
    {
      "id": "47ba0bb4-c9f8-49be-bd97-f79d5148c1da",
      "firstName": "Emily",
      "lastName": "Rodriguez",
      "linkedinUrl": "https://www.linkedin.com/in/emilyrodriguez/",
      "position": {
        "title": "Senior Software Engineer",
        "companyName": "Innovation Labs"
      },
      "headline": "Full-stack Engineer | Open Source Contributor",
      "score": 74,
      "scoreDetails": [
        "They overlapped for a long time (56 months) @ TechCorp, most recently in 2023",
        "They overlapped for a long time (45 months) @ StartupXYZ, most recently in 2016"
      ],
      "owners": [
        {
          "id": "0dda7490-eea3-4c1c-8cec-a4824222f116",
          "firstName": "John",
          "lastName": "Smith",
          "score": 72
        }
      ]
    }
  ],
  "count": 68,
  "nextPage": 2
}

Both cases above are worth reading twice. The first connection carries one relationships token but two relationshipDetails records — the mutual-connections record has no token of its own, which is why the two lists must never be zipped together. The second connection carries neither key: we hold no structured signal for that pair. That is a normal response, not an error — read scoreDetails.

Response DTO: IntegrationConnectionDto

Field Type Description
id string Unique identifier
firstName string First name
lastName string Last name
linkedinUrl string LinkedIn profile URL (optional)
position IntegrationPositionDto Current position
headline string LinkedIn headline (optional)
score number Relationship score (0-100, higher is better)
scoreDetails string[] Reasons for the relationship score, as finished English sentences. Unchanged by the addition of relationships / relationshipDetails — same strings, same order — and still the human-readable summary of the relationship. Absent when empty.
relationships string[] How the connector knows the target, as machine-readable tokens. Exactly three values are ever emitted: current_colleague (they are at the same company now), former_colleague (they were at the same company in the past), university_classmate (they attended the same school). More than one can apply. Absent when empty — present when we hold the signal for the pair, omitted when we do not, so read it as ?? []. Absence is not evidence of "no relationship" and says nothing about the strength of the connection; read scoreDetails.
relationshipDetails IntegrationRelationshipDetailDto[] The facts behind the relationship — one record per company, school, or mutual-connection signal, in no particular order. Absent when empty — present when we hold the records, omitted when we do not, so read it as ?? []. Independent of relationships: a mutualConnections record has no token counterpart, and a rank can carry tokens with no records.
owners IntegrationMemberDto[] Members of your team connected to this connector — i.e. who can request the intro on your behalf. Each score is that member's relationship strength (0–100) to the connector, strongest first.
avatarUrl string Absolute URL of the avatar image (LinkedIn-sourced via filestore), e.g. https://files-intros.draftboard.com/p/.... Omitted entirely when there is no stored avatar.
rating number Your own star rating of this connector, 1..5 where higher is better (5 = "ask anytime", 1 = "do not ask"), never a teammate's. Absent when you have not rated them — that is not a low rating. Filter on it with the rating query parameter.
labels array[string] The connector's labels as you see them, lowercase tokens — the same labels, with the same meaning, as labels on GET/supporters. Hidden labels are left out. Absent when the connector has no labels. Filter on them with connectorLabels / excludeConnectorLabels; the labels say which of them a connection matched.

Nested DTO: IntegrationRelationshipDetailDto

Exactly one of employment, education, mutualConnections is set on each record; the other two keys are absent. All dates in these shapes are ISO yyyy-MM-dd strings — day precision, no time and no timezone.

Field Type Description
employment IntegrationRelationshipEmploymentDto The two were at the same company or organisation. Present on employment records only.
education IntegrationRelationshipEducationDto The two attended the same school. Present on education records only.
mutualConnections IntegrationRelationshipMutualConnectionsDto The two share contacts. Present on mutual-connection records only. Note this kind has no counterpart in relationships — "mutual connections" is not one of the three relationship types.
score number This record's contribution to the connection's overall relationship score, as a fraction. Use it to rank records of the same kind against each other — e.g. show the strongest employment overlap first. It is not on the same scale as the connection's 0–100 score and does not sum to it. Absent when the producer did not record one.

Nested DTO: IntegrationRelationshipEmploymentDto

Nothing here is precomputed — derive what you need from the overlap window: they overlapped when overlapStartDate is present; they work together now when overlapStartDate is present and overlapEndDate is absent; duration and "most recently in <year>" come from the two dates.

Field Type Description
company string Company / organisation display name. Absent only when genuinely unknown.
department string Function or department context, when known (e.g. "Engineering").
location string Shared location, when known.
overlapStartDate string ISO yyyy-MM-dd. Start of the window in which both were at the company. Present means they overlapped in time. Absent together with overlapEndDate means same company but never at the same time — or a loose affiliation, which omits the window on purpose (see loose).
overlapEndDate string ISO yyyy-MM-dd. End of the shared window. Absent while the overlap is ongoing — a record with overlapStartDate set and no overlapEndDate means they are colleagues right now.
loose boolean true = a low-confidence same-organisation affiliation. The overlap window is deliberately omitted on these records, because that is exactly where the underlying position dates are least trustworthy — so do not read the missing window as "they never overlapped".
unit string Shared military unit, when one is detected.

Nested DTO: IntegrationRelationshipEducationDto

Field Type Description
school string School display name. Always present on an education record.
overlapStartDate string ISO yyyy-MM-dd. Start of the shared enrolment window. Absent together with overlapEndDate means both attended the school, but at different times.
overlapEndDate string ISO yyyy-MM-dd. Absent while both are currently students.

Nested DTO: IntegrationRelationshipMutualConnectionsDto

Field Type Description
count number Number of contacts the connector and the target share.

Nested DTO: IntegrationMemberDto

Field Type Description
id string The team member's id (the same id as teamMembers[].id on GET /me) — not the connector's id
firstName string First name
lastName string Last name
linkedinUrl string LinkedIn profile URL (optional)
score number Owner's relationship score with the connection (0-100)

Strongest Open Paths

GET /api/v1/integration/paths/strongest

Returns your strongest open paths — paths you have not yet requested — one row per target, strongest first. Each row carries the path itself (see the ConnectorIntroDto shape), the connector who can make the introduction (see the SupporterDto shape), and a basis explaining why the path ranks where it does. Once you request the intro, it drops out of this list. Read-only.

One row per target. When several connectors or several of your team's profiles could reach the same target, only the single best path for that target is returned.

Query Parameters

Parameter Type Description
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page, 1–100. Default: 20
accountId string Only targets at this account (id from GET /accounts).
tagIds array[string] Only targets carrying these tag ids.
tagNames array[string] Only targets carrying these tag names (alternative to tagIds). Matched case-insensitively.
tagMatch string How several tags combine: all (default) or any.
title string Case-insensitive substring of the target's title.
ownerIds array[string] Paths through these team members instead of you (member ids from GET /me).
rating array[number] Only paths through connectors you rated with one of these stars, 2..5 where higher is better, e.g. rating=5 (or filters[rating][]=5). Your own rating, also when ownerIds picks teammates. Connectors you have not rated are left out while the filter is set. 1 ("do not ask") is a 400: a connector you hid never appears here.
connectorLabels array[string] Only paths through a connector carrying these labels, e.g. connectorLabels=investor — "my strongest paths through our investors". Repeat it or comma-join it. Same tokens as labels on GET/supporters; an unknown token is a 400.
connectorLabelsMatch string How several connectorLabels combine: all (default) or any.
excludeConnectorLabels array[string] Paths through a connector carrying any of these labels are left out, e.g. excludeConnectorLabels=do_not_contact.
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/paths/strongest?pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetStrongestPathsResponseDto
{
  "status": 200,
  "errors": [],
  "paths": [
    {
      "path": {
        "id": "1b9d8f04-2c7a-4e3d-9a11-7f0c2d8e44aa",
        "score": 48,
        "scoreDetails": [
          "They overlapped for a long time (42 months) @ TechCorp, most recently in 2016"
        ],
        "target": {
          "id": "6f397389-dc11-4051-822a-5ffd25ec8417",
          "firstName": "Michael",
          "lastName": "Johnson",
          "linkedinUrl": "https://www.linkedin.com/in/michaeljohnson/",
          "position": {
            "title": "Chief Executive Officer",
            "companyName": "Tech Corp"
          },
          "headline": "CEO at Tech Corp | Building the future"
        }
      },
      "connector": {
        "id": "daf886d4-d2ca-4ca2-8cd7-5546c50ada5f",
        "firstName": "David",
        "lastName": "Chen",
        "linkedinUrl": "https://www.linkedin.com/in/davidchen/",
        "position": {
          "title": "Director of Engineering",
          "companyName": "Draftboard"
        }
      },
      "basis": "overlap_both_sides",
      "owners": [
        {
          "id": "c2e4a890-3f1b-4a2c-9d55-1e0f7a3b9c44",
          "firstName": "Sarah",
          "lastName": "Miller",
          "linkedinUrl": "https://www.linkedin.com/in/sarahmiller/",
          "score": 95
        }
      ]
    }
  ],
  "count": 1,
  "nextPage": 0
}

Response DTO: GetStrongestPathsResponseDto

Field Type Description
status number HTTP status code
errors string[] Error messages (empty on success)
paths StrongestPathDto[] One entry per target — see the table below. Paginated. Omitted when there are none (an empty list is not sent as []) — read it as response.paths ?? [].
count number Number of targets with a qualifying path — one path per target, so this is not a total path count.
nextPage number Next page number (0 if no more pages)

Nested DTO: StrongestPathDto

Field Type Description
path ConnectorIntroDto The intro itself — id, score, scoreDetails, target, and (when we hold the signal) relationships / relationshipDetails. Same shape as an item of GET /connectors/{connector_id}/intros — see the field reference there.
connector SupporterDto The connector who can make the introduction. Same shape as an entry of GET /supporters, including your personal tier / rating when you have one for this connector and its labels — see the field reference there.
basis string Why this path ranks where it does. Exactly one of three values:
  • overlap_both_sides — a shared employer or school appears both between you and the connector, and between the connector and the target.
  • overlap_connector_target — a shared employer or school appears between the connector and the target only.
  • other_quality_signal — some other quality signal supports the path; see path.scoreDetails for the specific reason.
owners IntegrationMemberDto[] Team members with a recorded relationship to this connector, strongest first. Same shape as owners on GET /connectors/{connector_id}/intros — see the nested DTO there.

Error Responses

  • 400 — an ownerIds entry is not a team member; an unknown tagIds or tagNames entry; resultPerPage outside 1–100.

Archive a Target

DELETE /api/v1/integration/targets/{target_id}

Archives a target (soft-delete via STATUS_DELETED) — equivalent to the "Archive" action in the Draftboard UI. The prospect row stays in the database; the target disappears from GET /api/v1/integration/targets and its capacity slot is released. Associated intros and indexing rows are not cascade-deleted — they remain as historical records. The action is not reversible through the public API. Returns 404 if target_id does not exist or belongs to another customer.

Path Parameters

Parameter Type Description
target_id required string (UUID) UUID of the target to delete
Example Request
curl -X 'DELETE' \
  'https://intros.draftboard.com/api/v1/integration/targets/6f397389-dc11-4051-822a-5ffd25ec8417' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}

Note: Despite the DELETE verb, this is an archive action — same behavior as clicking "Archive" in the Draftboard UI. The prospect row stays; only status moves to STATUS_DELETED. Historical intros and indexing rows are preserved.

Attach Tags to a Target

POST /api/v1/integration/targets/{target_id}/tags

Attach tags to an existing target by id or by name. Idempotent — attaching a tag the target already has is a no-op. Names are trimmed and auto-create as manual tags scoped to the calling customer; matches are case-insensitive (Acme and acme resolve to the same tag).

Path Parameters

Parameter Type Description
target_id required UUID Target id from GET /targets. Must belong to the calling customer.

Request Body

Field Type Description
tagIds UUID[] Existing tag ids belonging to the calling customer. Max 50.
tagNames string[] Tag names. Trimmed; auto-created if not yet present. Max 100 chars each; max 50 entries.

At least one of tagIds/tagNames must be non-empty. The combined deduped set is capped at 50 tags per request.

Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/targets/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/tags' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "tagIds": ["11111111-1111-4111-8111-111111111111"],
    "tagNames": ["acme-q3", "high-priority"]
  }'
Response: ActionResponseDto
{ "status": 200, "error": [] }

Error Responses

  • 404 — target_id is missing or owned by another customer.
  • 400 — any tagIds entry is missing or owned by another customer; the combined set is empty after dedupe/trim; the combined set exceeds 50.

Why this endpoint exists: POST /targets/import accepts tags on initial import, but re-importing an existing LinkedIn URL with new tags returns imported: 1 while the tag is silently dropped. Use this endpoint to attach tags to a target that already exists.

Attach Tags to Many Targets (Bulk)

POST /api/v1/integration/targets/tags

Bulk variant of POST /targets/{target_id}/tags. Attaches the same tag set to every target in targetIds. All-or-nothing — if any targetId is missing or owned by another customer, the entire request is rejected (404) before any write; same for foreign tagIds (400). Idempotent: re-running with the same payload is a no-op.

Request Body

Field Type Description
targetIds required UUID[] Target ids from GET /targets. Each must belong to the calling customer. 1–500 entries.
tagIds UUID[] Existing tag ids belonging to the calling customer. Max 50.
tagNames string[] Tag names. Trimmed; auto-created if not yet present. Max 100 chars each; max 50 entries.

At least one of tagIds/tagNames must be non-empty. The resolved deduped tag set is capped at 50. Combined work cap: targetIds × resolved tags ≤ 5000 associations.

Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/targets/tags' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "targetIds": [
      "daf886d4-d2ca-4ca2-8cd7-5546c50ada5f",
      "6f397389-dc11-4051-822a-5ffd25ec8417"
    ],
    "tagNames": ["acme-q3"]
  }'
Response: ActionResponseDto
{ "status": 200, "error": [] }

Error Responses

  • 404 — at least one targetId is missing or owned by another customer. The error message lists up to 10 offending ids plus a count of the rest.
  • 400 — body validation failure (empty targetIds, more than 500 targetIds, malformed UUID, etc.); any tagIds entry missing/foreign; resolved tag set empty or > 50; targetIds × tags exceeds 5000.

When to use this vs the single-target endpoint: after POST /targets/import returns N successfully-imported targets and you want to tag them all in one call. The single-target endpoint is fine for one-off updates from the UI.

Mark Intro as Requested

POST /api/v1/integration/intros/{intro_id}/requested

Marks an intro as requested (status moves to INTRO_SENT). Idempotent — re-running while already at INTRO_SENT returns 200 with no side effect.

Path Parameters

ParameterTypeDescription
intro_id required UUID Intro (path) id from GET /targets/{id}/connections or GET /connectors/{id}/intros.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/intros/{intro_id}/requested' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: ActionResponseDto
{ "status": 200, "error": [] }

Error Responses

  • 404 — intro_id is missing or owned by another customer or archived.
  • 400 — current intro status does not allow the transition (must be in the accept whitelist).

Mark Intro as Completed

POST /api/v1/integration/intros/{intro_id}/completed

Marks an intro as complete (status moves to INTRO_MADE). Idempotent — re-running while already at INTRO_MADE returns 200 with no side effect.

Path Parameters

ParameterTypeDescription
intro_id required UUID Intro (path) id from GET /targets/{id}/connections or GET /connectors/{id}/intros.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/intros/{intro_id}/completed' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: ActionResponseDto
{ "status": 200, "error": [] }

Error Responses

  • 404 — intro_id is missing or owned by another customer or archived.
  • 400 — current status not in complete whitelist (INTRO_SENT, MEETING_SCHEDULED, etc.).

Mark Intro as Declined

POST /api/v1/integration/intros/{intro_id}/declined

Marks an intro as declined (status moves to CUSTOMER_REJECTED). Idempotent — re-running while already at CUSTOMER_REJECTED returns 200 with no side effect.

Path Parameters

ParameterTypeDescription
intro_id required UUID Intro (path) id from GET /targets/{id}/connections or GET /connectors/{id}/intros.

Request Body

FieldTypeDescription
reasonId string Rejection reason. One of: connector_declined, prospect_declined, other (default). Defaults to other when omitted.
customReason string Free-text reason (≤ 200 chars).
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/intros/{intro_id}/declined' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{ "reasonId": "prospect_declined", "customReason": "Wrong company fit" }'
Response: ActionResponseDto
{ "status": 200, "error": [] }

Error Responses

  • 404 — intro_id is missing or owned by another customer or archived.
  • 400 — reasonId is not one of connector_declined / prospect_declined / other, or customReason exceeds 200 chars.

List Accounts (Company Search)

GET /api/v1/integration/accounts

Returns a paginated list of the companies (accounts) where you have saved targets, each with per-company counts: total saved targets, total connection paths, how many targets are reachable 1st- vs 2nd-degree, and score, the highest relationship rank among those paths. query selects which companies come back — by company name, or by a saved person's name or job title — and the counts on each one always describe the whole company. connectionDegree narrows to companies with a target at that degree, including none for the companies where you have saved people and hold no path at all. Results are sorted strongest-first. To list the individual targets at a company, call GET /targets?accountId=<id>.

Query Parameters

Parameter Type Description
query string Selects which companies are returned: matches a company name, or a saved target's name or job title. The counts on each returned company always cover the whole company, whichever way it matched. Min 2 characters.
connectionDegree string Narrow to companies that have a target at this degree. Available values: 1st (directly connected), 2nd (reachable through a connector), none (saved targets, no path held). 1st and 2nd overlap — a company can have targets at both. Independent of query: the person who matched the search need not be the one at that degree.
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page. Default: 20
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/accounts?query=Stripe&pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetAccountsResponseDto
{
  "status": 200,
  "errors": [],
  "accounts": [
    {
      "id": "7958f1c3-f3e6-4b32-8c32-227d01f032d7",
      "name": "Stripe",
      "linkedinUrl": "https://www.linkedin.com/company/stripe/",
      "targetsCount": 6,
      "pathsCount": 9,
      "firstDegreeCount": 2,
      "secondDegreeCount": 3,
      "score": 87,
      "logoUrl": "https://files-intros.draftboard.com/p/l/acme/logo/acme.png"
    }
  ],
  "count": 12,
  "nextPage": 0
}

Response DTO: IntegrationAccountDto

Field Type Description
id string Account (company) id. Use with GET /targets?accountId=<id> to list its targets.
name string Company name
linkedinUrl string Company LinkedIn URL (optional)
targetsCount number Number of saved targets at this company
pathsCount number Total connection paths across those targets
firstDegreeCount number Targets you or a teammate are directly connected to (1st degree)
secondDegreeCount number Targets reachable only via a connector (2nd degree)
score number Highest relationship rank (0–100) across the connection paths at this company — the value the list is sorted by. 0 when none of the paths carries a rank; it is not a statement that there is no path, which pathsCount tells you.
logoUrl string Absolute URL of the company logo, e.g. https://files-intros.draftboard.com/p/.... Omitted entirely when there is no stored logo.

Initiate Account Search

POST /api/v1/integration/search/accounts

Start a search for people with the given titles at the given companies. This initiates the search and returns a campaignId; people are discovered asynchronously, and there is no terminal "complete" signal (fire-and-forget). People found are normally added as targets automatically, up to a limit per company, and their paths are charged like any target's. Only the people beyond that limit wait in the pool for review — read them with GET /pool?campaignId={campaignId}, then confirm or reject them. Every call starts a new search, so do not repeat a call to retry it. Writing requires a funded credit balance — see Error Handling.

Body Parameters

ParameterTypeDescription
companies array[string] Companies to search — at least one. Each a company website/domain (acme.com) or a LinkedIn company URL (linkedin.com/company/acme). A value that is neither is dropped.
titles array[string] Job titles / roles to find at those companies (the search persona) — at least one.
name string Optional label for the search (shown app-side).
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/search/accounts' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{"companies":["acme.com","https://www.linkedin.com/company/acme"],"titles":["Head of Sales"]}'
Response: SearchAccountsResponseDto
{
  "status": 200,
  "errors": [],
  "campaignId": "b1e7…",         // people held for review: GET /pool?campaignId=b1e7…
  "imported": 2,                 // companies accepted into the search
  "notImportedAccounts": []      // passthrough from campaign creation; typically empty
}

Initiate Supporter Search

POST /api/v1/integration/search/supporters

Scan the networks of the given supporters (people you know, given as LinkedIn /in/ URLs) for people matching the given titles, optionally narrowed to specific companies. This initiates the search and returns a campaignId; people are discovered asynchronously. Everyone the search finds waits in the pool for your review — read them with GET /pool?campaignId={campaignId} and confirm the ones you want with POST /pool/confirm. Nothing becomes a target and nothing is charged until you confirm. The search does not change the rating of the supporters you send or of anyone it finds. There is no terminal "complete" signal (fire-and-forget), and no lifecycle emails are sent for API-sourced searches. Writing requires a funded credit balance — see Error Handling.

Body Parameters

ParameterTypeDescription
supporters array[string] Supporters to scan — at least one. Each a LinkedIn /in/ profile URL (e.g. https://www.linkedin.com/in/alice). Duplicates are deduped.
titles array[string] Job titles / roles to find in the supporters' networks (the search persona) — at least one.
companies array[string] Optional. Narrow the scan to specific companies (domain or LinkedIn company URL). Omit to scan the whole network.
name string Optional label for the search (shown app-side).
icp object Optional ideal customer. With enabled: true, every person the search finds is scored against it, and the fit appears as icpFits on GET/pool. Cannot be combined with companies — sending both returns 400.
icp.enabled boolean Required when icp is sent. false is the same as sending no icp. true needs at least one of the four fields below, or the request is a 400.
icp.industry string Optional. Industry of the ideal customer company, e.g. B2B SaaS. Up to 500 characters; surrounding spaces are trimmed and a blank value counts as not given.
icp.companySize string Optional. Company headcount, e.g. 50-1000, 200+ or SMB. Up to 500 characters.
icp.location string Optional. Company location, e.g. US, UK. Up to 500 characters.
icp.description string Optional. Anything else about the ideal customer. Up to 500 characters.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/search/supporters' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{"supporters":["https://www.linkedin.com/in/alice"],"titles":["Head of Sales"],"icp":{"enabled":true,"industry":"B2B SaaS","companySize":"50-1000"}}'
Response: SearchSupportersResponseDto
{
  "status": 200,
  "errors": [],
  "campaignId": "b1e7…",           // poll GET /pool?campaignId=b1e7…
  "importedSupporters": 1,         // supporter URLs accepted into the search
  "notImportedSupporters": 0
}

Note: results are asynchronous — poll GET/pool with campaignId={campaignId} for the people found — it fills in over time, so retry with backoff — then confirm the ones you want with POST/pool/confirm.

List Pool Prospects

GET /api/v1/integration/pool

This is your Queue. What the API calls the pool is shown as the Queue in the Draftboard web app — the same list of suggested prospects awaiting your review. The API path stays /pool.

Potential prospects in the pool — your Queue (status new) — awaiting confirm/reject, surfaced by auto-prospecting and searches. Everyone a supporter search finds appears here, and so does everyone an account search finds beyond its per-company limit. To follow a search: call GET /pool?campaignId={campaignId} with the campaignId from the search response, repeating with backoff until people appear, then send the ids you want to POST /pool/confirm. Use POST /pool/reject to discard the rest.

Query Parameters

Parameter Type Description
query string Substring match on LinkedIn URL
tagIds string[] Filter by tag IDs
tagMatch string How several tags combine: any returns prospects carrying at least one of them, all only those carrying every one. Default all.
campaignId string (uuid) Filter to a specific campaign
accountId string (uuid) Filter to a specific account (company)
minIcpFit integer Optional, 0–100. Only people the search given by campaignId scored at or above this fit; people it could not assess are left out. Requires campaignId — without it the request is a 400. Omitted, everyone is returned.
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page. Default: 20, max 100
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/pool?pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetPoolProspectsResponse
{
  "status": 200,
  "errors": [],
  "prospects": [
    {
      "id": "...",
      "status": "new",
      "name": "Jane Doe",
      "linkedinUrl": "https://www.linkedin.com/in/jane",
      "headline": "VP Sales",
      "source": "SOURCE_ACCOUNT_SEARCH",
      "tags": [{ "id": "...", "title": "Hot", "type": "manual" }],
      "createdAt": "2026-06-01T00:00:00.000Z",
      "accountName": "Stripe",
      "avatarUrl": "https://files-intros.draftboard.com/p/u/.../avatar/abc123.png",
      "icpFits": [
        { "campaignId": "b1e7…", "percent": 72, "reason": "industry ✓, location ✓" }
      ]
    }
  ],
  "count": 1,
  "nextPage": 0
}

ICP fit (icpFits)

Each prospect carries icpFits: one entry per supporter search that was sent an enabled icp and found this person. An entry is campaignId (the search), percent (0–100, how well the person's company matches that search's ideal customer) and reason (why). When percent is absent, the search could not assess this person — that is not a score of 0. An empty list means no such search found this person. The fit is per search: with campaignId, icpFits carries that search's entry only. Every prospect is returned whatever its fit, unless you ask for minIcpFit.

Error Responses

  • 400 — minIcpFit outside 0–100, or sent without campaignId.
  • 403 — missing, unknown or revoked API key.
  • 429 — read rate limit exceeded.

Confirm Pool Prospects

POST /api/v1/integration/pool/confirm

Confirm pool prospects (from your Queue, shown as Queue in the app) into saved targets. Every id you send that is still waiting in the pool becomes a target; from then on its paths are charged like any target's. confirmedCount is how many were confirmed by this call; remainingCapacity is for information only and does not limit what is confirmed. Idempotent — re-confirming an already-confirmed prospect is a no-op. Writing requires a funded credit balance.

Request Body

Field Type Description
ids required string[] Pool prospect UUIDs to confirm
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/pool/confirm' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{ "ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"] }'
Response: ConfirmPoolProspectsResponse
{
  "status": 200,
  "errors": [],
  "confirmedCount": 2,
  "remainingCapacity": 8
}

Error Responses

  • 403 — missing, unknown or revoked API key.
  • 402 — INSUFFICIENT_CREDITS in error[0]: the credit balance is exhausted. Top up to continue writing.
  • 429 — write rate limit exceeded.

Reject Pool Prospects

POST /api/v1/integration/pool/reject

Soft-deletes pending (status new) pool prospects from your Queue (shown as Queue in the app). Idempotent — rejecting an already-rejected prospect is a no-op. Confirmed prospects are unaffected (capacity-drift guard: a confirmed prospect is never silently un-counted). Writing requires a funded credit balance.

Request Body

Field Type Description
ids required string[] Pool prospect UUIDs to reject
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/pool/reject' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{ "ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"] }'
Response
{
  "status": 200,
  "error": []
}

Error Responses

  • 403 — missing, unknown or revoked API key.
  • 402 — INSUFFICIENT_CREDITS in error[0]: the credit balance is exhausted. Top up to continue writing.
  • 429 — write rate limit exceeded.

List Supporters

GET /api/v1/integration/supporters

Returns a paginated list of the customer's supporters — by default the full non-excluded connection network. Pass filters[preferred]=true to narrow the list to preferred supporters (connectors explicitly marked as preferred, prioritised by the matching engine). Pass filters[ownerIds][] to list only the LinkedIn connections of yourself or chosen teammates. Supports search by name.

Reading the whole list. One page holds at most 100 people, and most networks are larger. To read everything, request page 1, 2, 3 … until nextPage is 0; the order is stable, so each person comes back once and the number you read matches count. If people are added or removed while you page (for example while a network scan is running), a person can be skipped or repeated — read again once it has finished.

Note: This endpoint returns a view, not a persistent collection. Connectors with excluded = true are hidden. Use POST /api/v1/integration/connectors/{connector_id}/exclude to hide a connector and DELETE /api/v1/integration/connectors/{connector_id}/exclude to bring it back.

Hidden connectors — rating 1, "don't ask". Rating a connector 1 (equivalently tier 5) hides it: it is dropped from this listing by default, exactly as an excluded connector is. It is not gone — pass filters[rating][]=1 (or the equivalent filters[tiers][]=5) and the hidden connectors come back, each serialized with rating: 1 and tier: 5. The rating filter doubles as the "Hidden" scope; there is no separate flag, and default result sets are unchanged. One team exception: if a teammate keeps that connector visible, it stays in your default listing and carries your rating — rating: 1 — because you always read back your own rating, never a teammate's. Solo accounts never see this.

The consequence to design around: on a default listing rating is 2..5 or absent. If you rate someone 1 and re-list without the filter, they disappear rather than coming back with a 1. That is the rating working, not a write that failed.

Query Parameters

Parameter Type Description
filters[query] string Search by supporter name (matches first and last name)
filters[preferred] boolean When set, narrows the list. true = preferred supporters only, false = non-preferred only. Omit to return the full non-excluded network.
filters[tiers][] number[] Multi-select by your personal rating expressed as a cadence tier, 1..5, where lower is better. Repeat for several, e.g. filters[tiers][]=1&filters[tiers][]=2. Cumulative. Prefer filters[rating][] below, which counts the same way the app's stars do. Including 5 also opts into the hidden connectors (see the note above). Set a connector's tier via PUT /connectors/{connector_id}/tier.
filters[rating][] number[] The same filter the way the app shows it — 1..5, higher is better. Repeat for several, e.g. filters[rating][]=5&filters[rating][]=4. It mirrors filters[tiers][] exactly: filters[rating][]=5 returns the same set as filters[tiers][]=1. Passing both is a union, not an intersection. Including 1 also opts into the hidden connectors (see the note above).
filters[labels][] string[] Only connectors carrying these labels, e.g. filters[labels][]=investor. Repeat for several; how they combine is filters[labelsMatch]. Tokens: Org (shared with your team): investor, customer, advisor, partner, do_not_contact. Personal (yours only): close_friend, family, mentor, supporter (supporter = you rated them 2..5). Automatic: current_colleague, former_colleague, university_classmate. An unknown token is a 400. Hidden labels never match.
filters[labelsMatch] string How several filters[labels][] combine: all (default) = the connector carries every one, any = at least one.
filters[excludeLabels][] string[] Connectors carrying any of these labels are left out, e.g. filters[excludeLabels][]=do_not_contact.
filters[ownerIds][] string[] Only the people these team members are connected to on LinkedIn (1st degree). Member ids come from GET/me: your own is customerProfileId, your teammates' are teamMembers[].id. Repeat for several, e.g. filters[ownerIds][]=ID1&filters[ownerIds][]=ID2, to get anyone any of them is connected to. At most 50. An id that is not a UUID, or not an active member of your team, is a 400. Combines with every other filter. Omit it for the whole team's list, which also includes people marked as supporters without a LinkedIn connection.
paging[pageNumber] number Page number (1-based). Default: 1
paging[resultPerPage] number Results per page. Default: 20, maximum 100
Example Request — full non-excluded network
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/supporters?filters[query]=david&paging[pageNumber]=1&paging[resultPerPage]=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — preferred supporters only
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/supporters?filters[preferred]=true&paging[pageNumber]=1&paging[resultPerPage]=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — your top-rated connectors (★★★★★ and ★★★★)
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/supporters?filters[rating][]=5&filters[rating][]=4&paging[pageNumber]=1&paging[resultPerPage]=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — your investors and advisors, leaving out anyone marked do not contact
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/supporters?filters[labels][]=investor&filters[labels][]=advisor&filters[labelsMatch]=any&filters[excludeLabels][]=do_not_contact' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — your own LinkedIn connections (your customerProfileId from GET /me), 100 per page
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/supporters?filters[ownerIds][]=YOUR_CUSTOMER_PROFILE_ID&paging[pageNumber]=1&paging[resultPerPage]=100' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — the "Hidden" scope (connectors you rated ★ "don't ask")
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/supporters?filters[rating][]=1&paging[pageNumber]=1&paging[resultPerPage]=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetSupportersResponseDto
{
  "status": 200,
  "errors": [],
  "supporters": [
    {
      "id": "daf886d4-d2ca-4ca2-8cd7-5546c50ada5f",
      "firstName": "David",
      "lastName": "Chen",
      "linkedinUrl": "https://www.linkedin.com/in/davidchen/",
      "position": {
        "title": "Director of Engineering",
        "companyName": "Draftboard",
        "companyLinkedinUrl": "https://www.linkedin.com/company/draftboard/"
      },
      "headline": "Engineering Leader | Building great teams",
      "score": 85,
      "createdAt": "2026-04-15T10:30:00.000Z",
      "avatarUrl": "https://files-intros.draftboard.com/p/profiles/abc123/avatar/abc123.png",
      "tier": 1,
      "rating": 5,
      "labels": ["investor", "former_colleague", "supporter"],
      "owners": [
        {
          "id": "3f9c2a71-5b8e-4d0a-9c6f-7e1b2d4a8c90",
          "firstName": "Maria",
          "lastName": "Lopez",
          "linkedinUrl": "https://www.linkedin.com/in/marialopez/",
          "score": 85
        }
      ]
    }
  ],
  "count": 1,
  "nextPage": 0
}

Response DTO: IntegrationSupporterDto

Field Type Description
id string Connector ID (use this with /connectors/{connector_id}/* endpoints)
firstName string First name
lastName string Last name
linkedinUrl string LinkedIn profile URL (optional)
position IntegrationPositionDto Current position
headline string LinkedIn headline (optional)
score number Relationship rank (0-100) between authenticated customer and supporter
createdAt string Creation timestamp (ISO 8601)
avatarUrl string Absolute URL of the avatar image (LinkedIn-sourced via filestore), e.g. https://files-intros.draftboard.com/p/.... Omitted entirely when there is no stored avatar.
tier number Your personal rating for this connector on the cadence scale: 1 = "ask anytime" (the best), 2, 3, 4, 5 = "do not ask". Lower is better — see rating below for the same setting as the app shows it; the two are always consistent, and always present or absent together. Absent when unreviewed. A 5 is only ever returned when filters[rating][]=1 / filters[tiers][]=5 explicitly asked for hidden connectors. Set via PUT /connectors/{connector_id}/tier; personal to the API-key owner.
rating number The star rating, as the app shows it: 5 = ★★★★★ ("ask anytime"), 4 = ★★★★, 3 = ★★★, 2 = ★★, 1 = ★ ("don't ask"). Higher is better. tier and rating are two spellings of one setting, so rating is absent exactly when tier is absent (unreviewed). Never 0: "0 stars" is not a product state, so clearing a rating is PUT { "tier": 0 }, after which both fields are absent. On a default listing the value is 2..5; 1 appears only when filters[rating][]=1 asked for the hidden connectors. Personal to the API-key owner.
labels string[] Labels on this person as the API-key owner sees them, as lowercase tokens. Each label is listed once, even when it was set both on the person and on their company. Org labels — investor, customer, advisor, partner, do_not_contact — are shared across your team, including the ones that come from the company the person currently works at (set with PUT/companies/{company_id}/labels). Personal labels — close_friend, family, mentor, and supporter (you rated them 2..5) — are your own. Automatic labels — current_colleague, former_colleague, university_classmate — appear when anyone on your team has that relationship with the person. Hidden labels are left out: an automatic label you hid in the app, or a company label your team excepted for this person. An empty array means no labels. The same field, with the same meaning, is on every response that returns this shape: GET/supporters, GET /connectors, GET /connectors/{connector_id}/intros and GET /paths/strongest. Set labels on a person with PUT/connectors/{connector_id}/labels.
owners IntegrationMemberDto[] Members of your team who know this person, each with their relationship strength (score, 0-100) to them, strongest first — the same owners as on GET/targets/{target_id}/connections. Each id is a member id you can pass back as filters[ownerIds][]. Always the whole team, whatever filters[ownerIds][] asked for. It is broader than that filter, which counts LinkedIn connections only, so a teammate can be listed here for a person who is not in that teammate's filtered list. Filled on this endpoint, where an empty array means no teammate has a relationship with them; where this shape is the connector of another response, read that response's own owners instead.

Label and Rating Counts

GET /api/v1/integration/labels/counts

How your connections break down by label and by rating, so you can see what a filter on GET/supporters would return before asking for the list. For every label — 0 included — how many connections carry it; how many you rated at each rating 5..1 and how many you have not rated; and the total. Read-only.

Same filters as GET /supporters. Send any of its filters[…] — filters[query], filters[preferred], filters[ownerIds][] (a teammate's network), filters[rating][] / filters[tiers][], filters[labels][] / filters[labelsMatch] / filters[excludeLabels][] — and the counts describe that list.

Each section ignores its own filter. Label counts ignore the label filters (each is the list with only that label); rating counts and unrated ignore filters[rating][] and filters[tiers][] (rating 1 includes the connections it hides, as filters[rating][]=1 does). total applies every filter and equals the count GET /supporters returns.

Labels are counted as you see them: org labels team-wide, personal labels your own, hidden labels never.

Example Request — label counts in a teammate's network, leaving out do not contact
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/labels/counts?filters[ownerIds][]=c2e4a890-3f1b-4a2c-9d55-1e0f7a3b9c44&filters[excludeLabels][]=do_not_contact' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetLabelCountsResponseDto
{
  "status": 200,
  "errors": [],
  "labels": [
    { "label": "investor", "scope": "org", "count": 12 },
    { "label": "customer", "scope": "org", "count": 0 },
    { "label": "family", "scope": "personal", "count": 3 },
    { "label": "supporter", "scope": "personal", "count": 41 },
    { "label": "former_colleague", "scope": "personal", "count": 87 }
  ],
  "ratings": [
    { "rating": 5, "count": 9 },
    { "rating": 4, "count": 14 },
    { "rating": 3, "count": 11 },
    { "rating": 2, "count": 7 },
    { "rating": 1, "count": 2 }
  ],
  "unrated": 268,
  "total": 310
}

The example is shortened: labels always lists every label, in the order org, personal, supporter, automatic.

Response Fields

Field Type Description
labels LabelCountDto[] Every label: label (token, as in labels on a supporter), scope (org or personal) and count. A 0 is listed, never left out.
ratings RatingCountDto[] One entry per rating, 5 down to 1: rating (higher is better) and count.
unrated number Connections you have not rated.
total number Connections with every filter applied.

Error Responses

  • 400 — a filter value GET /supporters would refuse (an unknown label, a rating outside 1..5, an id in filters[ownerIds][] that is not on your team).
  • 403 — missing, unknown or revoked API key.
  • 412 — your own LinkedIn is not connected to Draftboard. Ratings, personal labels and supporter are counted as yours, so nothing is counted rather than half of it — read this as unknown, never as zero. Connect it in the app and retry.
  • 429 — read rate limit exceeded.

Import Supporters

POST /api/v1/integration/supporters/import

Import supporters by providing their LinkedIn URLs. Each URL is resolved to a connector and added to the customer's network.

Imported people are not rated. The import leaves ratings as they are: someone new stays unrated, and a rating you already gave someone is kept. To make someone a supporter, rate them 2..5 with PUT/connectors/{connector_id}/tier.

Request Body

Field Type Description
linkedinUrls required string[] LinkedIn profile URLs to import. Must contain at least one entry and at most 100 — an empty array, or more than 100, is rejected with 400 Bad Request. Split a larger list across several calls.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/supporters/import' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "linkedinUrls": [
      "https://www.linkedin.com/in/davidchen/",
      "https://www.linkedin.com/in/emilyrodriguez/"
    ]
  }'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}

Note: The response does not include per-URL results. After import, re-fetch GET /api/v1/integration/supporters to verify which supporters are now present in the list.

Toggle Preferred Flag for a Connector

POST /api/v1/integration/connectors/{connector_id}/prefer

Marks a connector as preferred. Preferred connectors are boosted by the matching engine when building intro paths. No request body.

Path Parameters

Parameter Type Description
connector_id required string (UUID) UUID of the connector. Obtain this from Connection.connector_id in any list response (e.g. /targets/{target_id}/connections or /supporters).
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/prefer' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}
DELETE /api/v1/integration/connectors/{connector_id}/prefer

Unmarks a connector as preferred (sets preferred = false). No request body.

Example Request
curl -X 'DELETE' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/prefer' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}

Note: Both endpoints return 404 Not Found if the supplied connector_id is not part of the authenticated customer's network.

Toggle Excluded Flag for a Connector

POST /api/v1/integration/connectors/{connector_id}/exclude

Marks a connector as excluded. Excluded connectors are hidden from default-view queries (e.g. they will not appear in GET /supporters) and are deprioritised by the matching engine. No request body.

Path Parameters

Parameter Type Description
connector_id required string (UUID) UUID of the connector. Obtain this from Connection.connector_id in any list response.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/exclude' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}
DELETE /api/v1/integration/connectors/{connector_id}/exclude

Unmarks a connector as excluded (sets excluded = false) so it reappears in default-view queries. No request body.

Example Request
curl -X 'DELETE' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/exclude' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}

Note: Both endpoints return 404 Not Found if the supplied connector_id is not part of the authenticated customer's network. preferred and excluded are independent flags — a connector can be both preferred and excluded simultaneously.

Set a Connector's Rating (Tier)

PUT /api/v1/integration/connectors/{connector_id}/tier

Sets your personal rating for a connector — the "star rating" that prioritises who you reach out to. Send it either as rating (the app's star scale, higher is better) or as tier (the internal cadence scale, lower is better); they are the same value. The rating is personal to the API-key owner: it is returned as both tier and rating on GET/supporters and GET/connectors, and is filterable via filters[rating][] / filters[tiers][].

Two spellings of one setting. rating counts the way the app's stars do — higher is better. tier is the internal cadence scale — lower is better. Pick whichever direction suits your client; the two never disagree.

rating — 5 = ★★★★★ "ask anytime" · 4 = ★★★★ · 3 = ★★★ · 2 = ★★ · 1 = ★ "don't ask".
tier — 1 is the best ("ask anytime") and 5 is "do not ask"; 0 clears the rating back to unreviewed.

Send exactly one of the two. A body carrying both rating and tier, or neither, is rejected with 400.

Clearing a rating stays { "tier": 0 }. rating has deliberately no zero — "0 stars" is not a state the product has — so there is no rating: 0 and no way to clear via rating. After clearing, both tier and rating are absent from read responses (back to unreviewed).

Rating a connector 1 hides it — that is the rating working, not a failed write. rating 1 / tier 5 is the "don't ask" rung: it excludes the connector from paths and drops it from the default GET/supporters and GET/connectors listings, so re-listing will not show it back with a 1. Read it back — and list everything you have hidden — with GET /supporters?filters[rating][]=1 (or the equivalent filters[tiers][]=5): the rating filter doubles as the "Hidden" scope, and there is no separate flag. To un-hide, rate the connector 2..5 or clear with { "tier": 0 }. One team exception: if a teammate keeps that connector visible, it stays in your default listing and carries your rating — rating: 1 — because you always read back your own rating, never a teammate's. Solo accounts never see this.

Path Parameters

Parameter Type Description
connector_id required string (UUID) UUID of the connector. Obtain this from Connection.connector_id in any list response.

Request Body: SetConnectorTierRequestDto

Exactly one of rating or tier is required. Sending both, sending neither, or sending a value outside its range returns 400.

Field Type Description
rating number The rating on the app's star scale — an integer 1–5, higher is better (see the scale above). There is no 0; to clear a rating use tier: 0. Send this or tier, never both.
tier number The same rating on the internal cadence scale — an integer 0–5, lower is better, where 0 clears the rating back to unreviewed. Send this or rating, never both. (Previously this field was required; presence has moved to the exactly-one-of rule, so existing tier-only clients are unaffected.)
Example Request — rate ★★★★★ "ask anytime" (the star scale)
curl -X 'PUT' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/tier' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -d '{ "rating": 5 }'
Example Request — the same thing on the tier scale
curl -X 'PUT' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/tier' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -d '{ "tier": 1 }'
Example Request — clear the rating (back to unreviewed)
curl -X 'PUT' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/tier' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -d '{ "tier": 0 }'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}

Note: Returns 404 Not Found if the supplied connector_id is not part of the authenticated customer's network, and 400 if the body carries both rating and tier, neither of them, or a value outside its range (rating 1–5, tier 0–5). Returns 412 when your own LinkedIn is not connected to Draftboard: a rating is personal, so there is no one to save it for, and nothing was written. Read the current rating back via the rating and tier fields on GET/supporters and GET/connectors, or list all connectors at a given rating with GET /supporters?filters[rating][]=5 (equivalently filters[tiers][]=1).

Label a Connector

PUT /api/v1/integration/connectors/{connector_id}/labels

Adds and removes labels on one person in your network — who they are to you. Labels are then returned on labels in GET/supporters and narrow that list (filters[labels][]) and the paths to a target (GET/targets/{target_id}/connections, connectorLabels). Labels only filter: they do not change what is scanned or which paths exist.

Who sees a label depends on the label, not on the call. Org labels — investor, customer, advisor, partner, do_not_contact — are facts about your organization's relationship with the person: your whole team sees them, and a teammate can remove one you added. Personal labels — close_friend, family, mentor — belong to the API-key owner and nobody else sees them.

Two kinds of label cannot be set here. supporter is your rating (2..5) — change it with PUT/connectors/{connector_id}/tier. The automatic labels — current_colleague, former_colleague, university_classmate — come from shared history. Sending either is a 400.

do_not_contact is descriptive. The person still appears in default lists; leave them out with filters[excludeLabels][]=do_not_contact or excludeConnectorLabels=do_not_contact.

Path Parameters

Parameter Type Description
connector_id required string (UUID) UUID of the connector — id on a supporter, connector_id on a connection.

Request Body

At least one of add or remove must carry a label, and the same label cannot be in both — either is a 400.

Field Type Description
add string[] Labels to add. Already there = nothing changes. Tokens are case-insensitive.
remove string[] Labels to remove. Not there = nothing changes.
Example Request — mark a connector as an investor
curl -X 'PUT' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/labels' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -d '{ "add": ["investor"] }'
Example Request — swap two personal labels
curl -X 'PUT' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/labels' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -d '{ "add": ["mentor"], "remove": ["close_friend"] }'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}

Error Responses

  • 400 — a malformed connector_id; an unknown token; supporter (set it with the rating, PUT/connectors/{connector_id}/tier); an automatic label (these cannot be set by hand); a label in both lists; or neither list carrying a label.
  • 404 — the connector is not part of your network (the same check as PUT /connectors/{connector_id}/tier).
  • 412 — your own LinkedIn is not connected to Draftboard, so there is no one to write the labels as; nothing was written. Connect it in the app and retry.
  • 402 — INSUFFICIENT_CREDITS: writing requires a funded credit balance — see Error Handling.
  • 403 — missing, unknown or revoked API key.

Label a Company

PUT /api/v1/integration/companies/{company_id}/labels

Labels a company for your whole team. Everyone in your team's network who currently works there carries the label — including people found later — and it shows on their labels and matches the label filters exactly like a label set on the person. Someone who leaves the company stops carrying it; a label you set on the person yourself stays.

Only org labels a company can carry: investor, customer, partner, do_not_contact. Anything else is a 400.

Take the id from GET/companies, which also tells you how many people the label reaches. One company can appear there as several rows, each with its own id: the label reaches only the people behind the row you label, so label every row that is the same company. An account id from GET/accounts is not a company id and is a 400.

A company nobody in your network works at yet can still be labelled; the label applies as soon as someone who works there is found.

Removing a label removes it from everyone who carried it through this company; the same label set directly on one of those people stays.

Path Parameters

Parameter Type Description
company_id required string (UUID) UUID of the company — an id from GET/companies. An account id is not a company id and is a 400.

Request Body

At least one of add or remove must carry a label, and the same label cannot be in both — either is a 400.

Field Type Description
add string[] Labels to add. Already there = nothing changes.
remove string[] Labels to remove. Not there = nothing changes.
Example Request — label a fund as an investor
curl -X 'PUT' \
  'https://intros.draftboard.com/api/v1/integration/companies/0c2f5f1e-8a2b-4c55-9d1e-3b7a1d2e4f60/labels' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -d '{ "add": ["investor"] }'
Response: BaseResponseDto
{
  "status": 200,
  "error": []
}

Error Responses

  • 400 — a malformed company_id, an account id in place of a company id, a token a company cannot carry, a label in both lists, or neither list carrying a label.
  • 412 — your own LinkedIn is not connected to Draftboard, so there is no one to write the labels as; nothing was written. Connect it in the app and retry.
  • 402 — INSUFFICIENT_CREDITS: writing requires a funded credit balance — see Error Handling.
  • 403 — missing, unknown or revoked API key.

Find Companies in Your Network

GET /api/v1/integration/companies

Returns the companies where someone in your team's network currently works, found by name and / or by label, with the people count first. Each row's id is the company_id PUT/companies/{company_id}/labels takes, and connectorsCount is how many people a label on that company reaches — read it before you label.

At least one of query or labels is required — without either the request is a 400.

One company can appear as several rows — for example "Sequoia Capital" with 7 people and "Sequoia" with 2 — each with its own id and count. A label reaches only the people behind the row you label, so label every row that is the same company.

Not an account. These are the employers of the people you know, not the companies of your saved targets — an account id from GET/accounts is not a company id.

Query Parameters

Parameter Type Description
query string Company name, case-insensitive, matched at the start of a word: sequoia finds "Sequoia Capital". Up to 200 characters.
labels array[string] Only companies carrying these labels: investor, customer, partner, do_not_contact. Repeat it or comma-join it. Any other token is a 400. A labelled company nobody in your network works at now is included too, with connectorsCount 0 and no name.
labelsMatch string How several labels combine: all (default) = the company carries every one, any = at least one.
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page. Default: 20, max: 100
Example Request — find Sequoia before labelling it
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/companies?query=sequoia&pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetNetworkCompaniesResponseDto
{
  "status": 200,
  "errors": [],
  "companies": [
    {
      "id": "0c2f5f1e-8a2b-4c55-9d1e-3b7a1d2e4f60",
      "name": "Sequoia Capital",
      "connectorsCount": 7,
      "labels": ["investor"]
    },
    {
      "id": "6a1d9e2b-3f4c-4b8a-9e7d-1c2b3a4d5e6f",
      "name": "Sequoia",
      "connectorsCount": 2
    }
  ],
  "count": 2,
  "nextPage": 0
}

Response DTO: IntegrationNetworkCompanyDto

Field Type Description
id string (UUID) Company id — the company_id for PUT/companies/{company_id}/labels.
name string Company name as the people's positions spell it. Absent when nobody in your network works there now.
connectorsCount number People in your network who currently work here — who a label on this company reaches. Always present; 0 when nobody works there now. Rows come most people first.
labels array[string] Labels on this company, lowercase tokens. Absent when the company has none.

Error Responses

  • 400 — neither query nor labels, a token a company cannot carry, an unknown labelsMatch, or a page size outside 1..100.
  • 403 — missing, unknown or revoked API key.

Get Intros for a Connector

GET /api/v1/integration/connectors/{connector_id}/intros

Returns the connector-first view of the network: the queried connector and its owners (team members who can reach it) are returned once at the top level, with a paginated items array of intros — one per target the connector can introduce. Each item carries only the per-intro fields (id, score, scoreDetails, relationships, relationshipDetails, target); the connector data is not repeated per row.

Each item carries the same structured relationship fields as GET/targets/{target_id}/connections: relationships (current_colleague / former_colleague / university_classmate) and relationshipDetails (the employment / education / mutual-connection facts, ISO yyyy-MM-dd dates). The full field reference and the nested shapes are documented once, on that endpoint.

The same absence rule applies. Both fields are present when we hold that signal for the pair and omitted when we do not. An absent field means "we have no structured signal for this pair", not "no relationship", and is not a statement about the strength of the connection — scoreDetails is unchanged and carries the human-readable summary.

Empty means absent, not [] — read them as item.relationships ?? []. And the two fields are independent: a mutual-connections-only signal produces a relationshipDetails record with no relationships entry, so never index-align one against the other or against scoreDetails.

Path Parameters

Parameter Type Description
connector_id required string (UUID) UUID of the connector

Query Parameters

Parameter Type Description
paging[pageNumber] number Page number (1-based). Default: 1
paging[resultPerPage] number Results per page. Default: 20
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/connectors/daf886d4-d2ca-4ca2-8cd7-5546c50ada5f/intros?paging[pageNumber]=1&paging[resultPerPage]=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetConnectorIntrosResponseDto
{
  "status": 200,
  "count": 1,
  "nextPage": 0,
  "connector": {
    "id": "daf886d4-d2ca-4ca2-8cd7-5546c50ada5f",
    "firstName": "David",
    "lastName": "Chen",
    "linkedinUrl": "https://www.linkedin.com/in/davidchen/",
    "position": {
      "title": "Director of Engineering",
      "companyName": "Draftboard"
    },
    "headline": "Director of Engineering at Draftboard",
    "createdAt": "2026-02-06T10:45:20.359Z",
    "avatarUrl": "https://files-intros.draftboard.com/p/profiles/abc123/avatar/abc123.png",
    "tier": 2,
    "rating": 4,
    "labels": ["advisor"]
  },
  "owners": [
    {
      "id": "c2e4a890-3f1b-4a2c-9d55-1e0f7a3b9c44",
      "firstName": "Sarah",
      "lastName": "Miller",
      "linkedinUrl": "https://www.linkedin.com/in/sarahmiller/",
      "score": 95
    }
  ],
  "items": [
    {
      "id": "1b9d8f04-2c7a-4e3d-9a11-7f0c2d8e44aa",
      "score": 48,
      "scoreDetails": [
        "They overlapped for a long time (42 months) @ TechCorp, most recently in 2016",
        "They attended Stanford University at the same time"
      ],
      "relationships": [
        "former_colleague",
        "university_classmate"
      ],
      "relationshipDetails": [
        {
          "employment": {
            "company": "TechCorp",
            "department": "Engineering",
            "overlapStartDate": "2012-09-01",
            "overlapEndDate": "2016-03-01",
            "loose": false
          },
          "score": 0.42
        },
        {
          "education": {
            "school": "Stanford University",
            "overlapStartDate": "2004-09-01",
            "overlapEndDate": "2008-06-01"
          },
          "score": 0.19
        }
      ],
      "target": {
        "id": "6f397389-dc11-4051-822a-5ffd25ec8417",
        "firstName": "Michael",
        "lastName": "Johnson",
        "linkedinUrl": "https://www.linkedin.com/in/michaeljohnson/",
        "position": {
          "title": "Chief Executive Officer",
          "companyName": "Tech Corp"
        },
        "headline": "CEO at Tech Corp | Building the future",
        "avatarUrl": "https://files-intros.draftboard.com/p/profiles/abc123/avatar/abc123.png"
      }
    }
  ]
}

Response DTO: GetConnectorIntrosResponseDto

Field Type Description
status number HTTP status code
errors string[] Error messages (empty on success)
connector SupporterDto The connector being queried (same as connector_id). Returned on every successful response, including empty pages. Carries the same shape as IntegrationSupporterDto, including your personal tier and rating when you have rated this connector (both absent when unreviewed) and its labels, with the same meaning as on GET/supporters.
owners IntegrationMemberDto[] Team members in your organization who have a recorded relationship to this connector. Each score is that member's relationship rank (0–100) to the connector.
items ConnectorIntroDto[] One entry per intro for this connector — see the table below. Paginated.
count number Total number of intros for this connector
nextPage number Next page number (0 if no more pages)

Nested DTO: ConnectorIntroDto

Field Type Description
id string Intro id — the value the /intros/{intro_id}/* and shortlist routes take.
score number Relationship rank (0–100) between the connector and this target.
scoreDetails string[] Reasons for the score, as finished English sentences. Unchanged by the addition of the two structured fields below, and still the human-readable summary of the relationship. Absent when empty.
relationships string[] How the connector knows this target: current_colleague, former_colleague, university_classmate. No other value is ever emitted. Absent when empty — present when we hold the signal for the pair, omitted when we do not. See the note at the top of this section, and the full description.
relationshipDetails IntegrationRelationshipDetailDto[] The facts behind the relationship — one record per company, school, or mutual-connection signal, with exactly one of employment / education / mutualConnections set and ISO yyyy-MM-dd dates. Absent when empty. Shapes documented under Get Target Connections.
target TargetSummaryDto The target this intro reaches — see below.

Nested DTO: TargetSummaryDto

Field Type Description
id string Target id
firstName string First name
lastName string Last name
linkedinUrl string LinkedIn profile URL (optional)
position IntegrationPositionDto Current position
headline string LinkedIn headline (optional)
avatarUrl string Absolute URL of the avatar image (LinkedIn-sourced via filestore), e.g. https://files-intros.draftboard.com/p/.... Omitted entirely when there is no stored avatar.

Note: Returns 404 Not Found if the supplied connector_id is not part of the authenticated customer's network. For a valid connector with no intros (or a page past the end), returns 200 with connector and owners populated and an empty items array.

List Connectors by Target Tags

GET /api/v1/integration/connectors

Returns a paginated list of your connectors filtered by the tags of the targets they can introduce, each with the matching targets. Use tagIds or tagNames to narrow results, and tagMatch to say how several tags combine. labels / excludeLabels narrow by who the connector is to you; together with tags, a connector must satisfy both.

Several tags: With tagMatch=all (the default) and tagIds=[VIP,Hot], only connectors who can introduce you to at least one VIP-tagged target and at least one Hot-tagged target are returned (the covered targets may be different for each tag). With tagMatch=any, every connector who can introduce you to at least one target carrying any of the tags is returned. The targets listed under each connector are always those carrying any of the requested tags. Without any tag filter, all connectors with at least one reachable target are returned. An unknown or foreign tag ID or name returns 400.

Ratings on this endpoint. Each connector carries your personal tier and rating — two spellings of one setting. rating is 1..5 with higher better and is never 0; tier is 1..5 with lower better. Both are absent when you have not rated the connector. Set them with PUT/connectors/{connector_id}/tier.

Hidden connectors are normally not listed here. A connector rated 1 ("don't ask", equivalently tier 5) is dropped from the default listings, and this endpoint has no rating filter with which to ask for it — so rating here is 2..5 or absent — unless a teammate keeps that connector visible, in which case it remains listed carrying your own rating: 1. To list what you have hidden, use GET/supporters with filters[rating][]=1 (or the equivalent filters[tiers][]=5), where the rating filter doubles as the "Hidden" scope.

Query Parameters

Parameter Type Description
tagIds array[string] Target tag IDs. How several combine is tagMatch.
tagNames array[string] Target tag names (resolved to IDs; case-insensitive; must belong to your account). Optional.
tagMatch string How several tags combine: with all (the default), a connector is returned only if it covers every tag — for each tag, at least one of its eligible targets carries it, and the targets may differ per tag. With any, a connector is returned if it can introduce you to at least one target carrying any of the tags. Default all.
labels array[string] Only connectors carrying these labels, e.g. labels=investor. Repeat it or comma-join it. Same tokens as labels on GET/supporters; an unknown token is a 400.
labelsMatch string How several labels combine: all (default) = the connector carries every one, any = at least one.
excludeLabels array[string] Connectors carrying any of these labels are left out, e.g. excludeLabels=do_not_contact.
pageNumber number Page number (1-based). Default: 1
resultPerPage number Results per page. Default: 20, max: 100
Example Request — connectors covering all VIP-tagged targets
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/connectors?tagNames=VIP&pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Example Request — all connectors, no tag filter
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/connectors?pageNumber=1&resultPerPage=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetConnectorsResponseDto
{
  "status": 200,
  "errors": [],
  "count": 1,
  "nextPage": 0,
  "connectors": [
    {
      "connector": {
        "id": "daf886d4-d2ca-4ca2-8cd7-5546c50ada5f",
        "firstName": "David",
        "lastName": "Chen",
        "linkedinUrl": "https://www.linkedin.com/in/davidchen/",
        "position": {
          "title": "Director of Engineering",
          "companyName": "Draftboard",
          "companyLinkedinUrl": "https://www.linkedin.com/company/draftboard/"
        },
        "headline": "Engineering Leader | Building great teams",
        "score": 85,
        "createdAt": "2026-04-15T10:30:00.000Z",
        "avatarUrl": "https://files-intros.draftboard.com/p/profiles/abc123/avatar/abc123.png",
        "tier": 1,
        "rating": 5,
        "labels": ["investor", "supporter"]
      },
      "targets": [
        {
          "id": "6f397389-dc11-4051-822a-5ffd25ec8417",
          "firstName": "Michael",
          "lastName": "Johnson",
          "linkedinUrl": "https://www.linkedin.com/in/michaeljohnson/",
          "position": {
            "title": "Chief Executive Officer",
            "companyName": "Tech Corp"
          },
          "headline": "CEO at Tech Corp | Building the future",
          "avatarUrl": "https://files-intros.draftboard.com/p/profiles/def456/avatar/def456.png"
        },
        {
          "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
          "firstName": "Sarah",
          "lastName": "Williams",
          "linkedinUrl": "https://www.linkedin.com/in/sarahwilliams/",
          "position": {
            "title": "VP of Sales",
            "companyName": "Tech Corp"
          },
          "headline": "VP Sales | Revenue Growth Expert",
          "avatarUrl": null
        }
      ]
    }
  ]
}

Response DTO: GetConnectorsResponseDto

Field Type Description
status number HTTP status code
errors string[] Error messages (empty on success)
count number Total number of connectors matching the filter
nextPage number Next page number (0 if no more pages)
connectors ConnectorWithTargetsDto[] Paginated list of connectors with their matching targets

Nested DTO: ConnectorWithTargetsDto

Field Type Description
connector IntegrationSupporterDto The connector profile: id, firstName, lastName, linkedinUrl, position, headline, score, createdAt, avatarUrl, tier, rating, labels. tier and rating are your personal rating for the connector — the same pair documented on GET/supporters — and both are absent when the connector is unreviewed. labels has the same meaning as there.
targets TargetSummaryDto[] The targets this connector can introduce you to that match the requested tag filter. Each target has: id, firstName, lastName, linkedinUrl, position, headline, avatarUrl

List Shortlist Entries

GET /api/v1/integration/shortlist

The shortlist is a team-shared list of pinned intros. Each entry pins one intro (path) — a possible warm introduction between a connector and a target — so the team can rally around the best opportunities, assign an owner, and track them to completion. Entries are scoped to the organization (the org owner), so every teammate sees the same shortlist.

Returns a paginated list of the team's shortlist entries. Filter by assignee, account, connector, intro state, or staleness.

Query Parameters

Filters use bracket notation (filters[…]); paging uses paging[…].

Parameter Type Description
filters[assigneeCustomerProfileId] string (uuid) Only entries currently assigned to this team member (customer profile id)
filters[accountId] string (uuid) Only entries whose intro targets this account (company) id — the id from GET /accounts, not the LinkedIn company id
filters[connectorId] string (uuid) Only entries whose intro goes through this connector
filters[introState] string Intro lifecycle state: one of open, sent, made, declined
filters[stale] boolean true = only stale entries (untouched for at least 14 days), false = only fresh ones. Omit to return both.
paging[pageNumber] number Page number (1-based). Default: 1
paging[resultPerPage] number Results per page. Default: 20, max 100
Example Request
curl -X 'GET' \
  'https://intros.draftboard.com/api/v1/integration/shortlist?filters[introState]=open&paging[pageNumber]=1&paging[resultPerPage]=20' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: GetShortlistResponseDto
{
  "status": 200,
  "errors": [],
  "entries": [
    {
      "id": "9b1c0e2a-7c4d-4a9f-9d2b-1f0a3c5e7b8d",
      "introId": "6f397389-dc11-4051-822a-5ffd25ec8417",
      "ownerCustomerProfileId": "1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809",
      "currentAssigneeProfileId": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80910",
      "note": "Warm intro — knows the VP personally",
      "createdAt": "2026-06-01T09:24:58.468Z",
      "updatedAt": "2026-06-10T12:40:12.456Z",
      "stale": false,
      "pastAssigneeProfileIds": []
    }
  ],
  "count": 1,
  "nextPage": 0
}

Note: pastAssigneeProfileIds is populated only in single-entry mutation responses (add / reassign) and the in-app groups feed; it is empty in this flat list.

Response DTO: ShortlistEntryDto

Field Type Description
id string Shortlist entry id
introId string Intro (path) id this entry pins
ownerCustomerProfileId string Org owner profile id that scopes this team-shared entry
currentAssigneeProfileId string Team member currently assigned; absent on solo accounts
note string Personal memo (self-assigned) or handoff message (assigned to a teammate). Optional
createdAt string Creation timestamp (ISO 8601)
updatedAt string Last update timestamp (ISO 8601)
stale boolean true when the open entry has been untouched for at least 14 days
pastAssigneeProfileIds string[] Anti-bounce: profiles ever assigned to this entry. Populated only in single-entry mutation responses and the groups feed; empty in this flat list

Error Responses

  • 403 — missing, unknown or revoked API key.
  • 429 — read rate limit exceeded.

Add an Intro to the Shortlist

POST /api/v1/integration/shortlist/intros/{intro_id}

Pins an intro (path) to the team-shared shortlist. Idempotent — re-adding an already-shortlisted intro returns 200 with the existing entry (a differing note is applied on re-add). On team accounts, assigneeCustomerProfileId is required and must be an active member of the organization. On solo accounts, omit it or pass your own customer profile id. Writes require a funded credit wallet (402 INSUFFICIENT_CREDITS when the wallet is exhausted).

Path Parameters

ParameterTypeDescription
intro_id required UUID Intro (path) id from GET /targets/{id}/connections or GET /connectors/{id}/intros.

Request Body

FieldTypeDescription
assigneeCustomerProfileId string (uuid) Team member to assign the entry to. Required on team accounts (must be an active org member, else 400). On solo accounts: omit it, or pass your own profile id — any other id is rejected with 400.
note string Personal memo (self-assigned) or handoff message (assigned to a teammate). Max 2000 chars.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/shortlist/intros/{intro_id}' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{ "assigneeCustomerProfileId": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80910", "note": "Warm intro — knows the VP personally" }'
Response: ShortlistEntryResponseDto
{
  "status": 200,
  "error": [],
  "entry": {
    "id": "9b1c0e2a-7c4d-4a9f-9d2b-1f0a3c5e7b8d",
    "introId": "6f397389-dc11-4051-822a-5ffd25ec8417",
    "ownerCustomerProfileId": "1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809",
    "currentAssigneeProfileId": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80910",
    "note": "Warm intro — knows the VP personally",
    "createdAt": "2026-06-01T09:24:58.468Z",
    "updatedAt": "2026-06-01T09:24:58.468Z",
    "stale": false,
    "pastAssigneeProfileIds": ["2b3c4d5e-6f70-8192-a3b4-c5d6e7f80910"]
  }
}

Error Responses

  • 400 — on team accounts: assigneeCustomerProfileId missing or not an active org member. On solo accounts: a foreign assigneeCustomerProfileId was passed.
  • 402 — INSUFFICIENT_CREDITS: the org credit wallet is exhausted; top up to continue writing.
  • 404 — intro_id does not exist or belongs to another customer.

Remove an Intro from the Shortlist

DELETE /api/v1/integration/shortlist/intros/{intro_id}

Removes the open shortlist entry for this intro (unpins it). Writes require a funded credit wallet (402 INSUFFICIENT_CREDITS when the wallet is exhausted).

Path Parameters

ParameterTypeDescription
intro_id required UUID Intro (path) id of the shortlist entry to remove.
Example Request
curl -X 'DELETE' \
  'https://intros.draftboard.com/api/v1/integration/shortlist/intros/{intro_id}' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: ActionResponseDto
{ "status": 200, "error": [] }

Error Responses

  • 402 — INSUFFICIENT_CREDITS: the org credit wallet is exhausted; top up to continue writing.
  • 404 — there is no open shortlist entry for this intro.

Reassign a Shortlist Entry

POST /api/v1/integration/shortlist/intros/{intro_id}/reassign

Hands the open entry for this intro to another team member, optionally with a handoff note. An anti-bounce rule prevents reassigning an entry to a member it was previously assigned to (the org owner bypasses this rule). Writes require a funded credit wallet (402 INSUFFICIENT_CREDITS when the wallet is exhausted).

Path Parameters

ParameterTypeDescription
intro_id required UUID Intro (path) id of the shortlist entry to reassign.

Request Body

FieldTypeDescription
assigneeCustomerProfileId required string (uuid) Team member to hand the entry to. Must be a member of the organization.
note string Optional handoff message for the new assignee. Max 2000 chars.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/shortlist/intros/{intro_id}/reassign' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{ "assigneeCustomerProfileId": "3c4d5e6f-7081-92a3-b4c5-d6e7f8091021", "note": "You have the closest relationship here" }'
Response: ShortlistEntryResponseDto
{
  "status": 200,
  "error": [],
  "entry": {
    "id": "9b1c0e2a-7c4d-4a9f-9d2b-1f0a3c5e7b8d",
    "introId": "6f397389-dc11-4051-822a-5ffd25ec8417",
    "ownerCustomerProfileId": "1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809",
    "currentAssigneeProfileId": "3c4d5e6f-7081-92a3-b4c5-d6e7f8091021",
    "note": "You have the closest relationship here",
    "createdAt": "2026-06-01T09:24:58.468Z",
    "updatedAt": "2026-06-11T08:15:00.000Z",
    "stale": false,
    "pastAssigneeProfileIds": ["2b3c4d5e-6f70-8192-a3b4-c5d6e7f80910", "3c4d5e6f-7081-92a3-b4c5-d6e7f8091021"]
  }
}

Error Responses

  • 400 — the assignee is not a member of the organization.
  • 402 — INSUFFICIENT_CREDITS: the org credit wallet is exhausted; top up to continue writing.
  • 404 — there is no open shortlist entry for this intro.
  • 422 — anti-bounce: the entry was previously assigned to this member (the org owner bypasses this rule).

Reject a Shortlist Entry

POST /api/v1/integration/shortlist/intros/{intro_id}/reject

Permanently dismisses this (connector, target) pair — it is removed from the shortlist and is not suggested for shortlisting again. Only the current assignee or the org owner may reject. Writes require a funded credit wallet (402 INSUFFICIENT_CREDITS when the wallet is exhausted).

Path Parameters

ParameterTypeDescription
intro_id required UUID Intro (path) id of the shortlist entry to reject.
Example Request
curl -X 'POST' \
  'https://intros.draftboard.com/api/v1/integration/shortlist/intros/{intro_id}/reject' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_TOKEN_HERE'
Response: ActionResponseDto
{ "status": 200, "error": [] }

Error Responses

  • 403 — caller is neither the current assignee nor the org owner.
  • 402 — INSUFFICIENT_CREDITS: the org credit wallet is exhausted; top up to continue writing.
  • 404 — there is no open shortlist entry for this intro.

DTO Reference

Quick reference for all Data Transfer Objects used in the Integration API.

Response DTOs

DTO Name Used In Description
GetMeResponseDto GET /integration/me Customer information response
GetTagsResponseDto GET /integration/tags Paginated list of tags
GetTargetsResponseDto GET /integration/targets Paginated list of targets
ImportTargetsResponseDto POST /integration/targets/import Import results with success/failure counts
GetConnectionsResponseDto GET /integration/targets/{id}/connections Paginated list of connections
BaseResponseDto / ActionResponseDto DELETE /integration/targets/{id}, POST /integration/targets/{id}/tags, POST /integration/targets/tags, POST /integration/supporters/import, POST/DELETE /integration/connectors/{id}/prefer, POST/DELETE /integration/connectors/{id}/exclude, POST /integration/intros/{id}/requested · /completed · /declined, DELETE /integration/shortlist/intros/{id}, POST /integration/shortlist/intros/{id}/reject Standard envelope with status and error (singular, repeated string — empty on success)
GetPoolProspectsResponseDto GET /integration/pool Paginated list of pool prospects (the app's Queue) awaiting confirm/reject
ConfirmPoolProspectsResponseDto POST /integration/pool/confirm Confirm result with confirmedCount and remainingCapacity
GetSupportersResponseDto GET /integration/supporters Paginated list of supporters
GetConnectorIntrosResponseDto GET /integration/connectors/{id}/intros Paginated list of intros for a connector (connector-first view)
GetNetworkCompaniesResponseDto GET /integration/companies Paginated list of companies in your network (companies)
GetShortlistResponseDto GET /integration/shortlist Paginated list of shortlist entries (entries)
ShortlistEntryResponseDto POST /integration/shortlist/intros/{id}, POST /integration/shortlist/intros/{id}/reassign Single shortlist entry after add/reassign

Entity DTOs

DTO Name Description
IntegrationCustomerDto Customer/company information with user, subscription, and token details
IntegrationUserDto User profile (API key owner)
IntegrationSubscriptionDto Subscription details: planCode, planName, seatLimit, seatUsed, copilotEnabled
IntegrationTagDto Tag with id, title, and type
IntegrationTargetDto Target profile with status, position, score, connection degree, and tags
IntegrationAccountDto Account (company) summary with target/path counts and 1st/2nd-degree counts
IntegrationNetworkCompanyDto A company people in your network work at: the id to label, its name, the people count and its labels
IntegrationConnectionDto Connection with score, score details, owners, the connector's labels, and — when the rank carries them — the structured relationships tokens and relationshipDetails records
IntegrationRelationshipDetailDto One structured relationship signal between connector and target. Exactly one of employment, education, mutualConnections is set, plus an optional score (this record's contribution, for ranking same-kind records against each other). Absent when empty — the key is omitted, never [].
IntegrationRelationshipEmploymentDto Same-company signal: company, department, location, overlapStartDate, overlapEndDate, loose, unit. Dates are ISO yyyy-MM-dd strings. Overlap present ⇒ they overlapped; overlapEndDate absent ⇒ still colleagues; loose: true ⇒ low-confidence affiliation with the window deliberately omitted.
IntegrationRelationshipEducationDto Same-school signal: school, overlapStartDate, overlapEndDate (ISO yyyy-MM-dd). Both dates absent ⇒ attended at different times.
IntegrationRelationshipMutualConnectionsDto Shared-contacts signal: count. Has no counterpart token in relationships.
IntegrationPositionDto Job position (title, company name, company URL)
IntegrationMemberDto Team member/connector information with relationship score
IntegrationSupporterDto Supporter — a connector in the customer's network. Returned by GET /supporters and GET /connectors; use filters[preferred]=true to narrow to preferred supporters only. Carries your personal rating as both tier (1..5, lower is better) and rating (1..5, higher is better) — two spellings of one setting; both absent when unreviewed. It also carries labels — who the person is to you — on every endpoint that returns it (see Label a Connector)
TargetSummaryDto Compact target description embedded in connector-first intro responses
ConnectorIntroDto Single intro in the connector-first view: intro id, score, scoreDetails, relationships, relationshipDetails, and target
ShortlistEntryDto A pinned intro on the team shortlist: introId, ownerCustomerProfileId, currentAssigneeProfileId, note, stale, pastAssigneeProfileIds

Request DTOs

DTO Name Used In Description
ImportTargetsRequestDto POST /integration/targets/import LinkedIn URLs and optional tags to import
ImportSupportersRequestDto POST /integration/supporters/import LinkedIn URLs of supporters to import (min 1 entry)
SetConnectorTierRequestDto PUT /integration/connectors/{id}/tier Exactly one of rating (1–5, higher is better) or tier (0–5, lower is better, 0 clears). Both or neither returns 400
AddToShortlistBodyDto POST /integration/shortlist/intros/{id} Optional assigneeCustomerProfileId and note (≤ 2000 chars)
ReassignShortlistBodyDto POST /integration/shortlist/intros/{id}/reassign Required assigneeCustomerProfileId and optional note (≤ 2000 chars)

Pagination

All list endpoints support pagination using the following parameters:

Parameter Default Description
pageNumber 1 Page number (1-based indexing)
resultPerPage 20 Number of results per page

The response includes:

Rate Limits

The Integration API enforces per-customer rate limits using two independent buckets that share a 60-second window:

Bucket Limit (requests / 60s) Applies to
read 50 All GET endpoints (e.g. /me, /targets, /supporters, /connectors/{id}/intros)
write 30 All mutating endpoints (e.g. POST /targets/import, POST /supporters/import, DELETE /targets/{id}, prefer/exclude toggles)

Important: write requests consume from both buckets. Mutating endpoints are counted against the write bucket (the stricter ceiling trips first) and against the read bucket. A burst of 30 writes within 60 seconds also burns 30 of the 50 read slots. Plan read polling around your write volume, or stagger writes if your client mixes both.

Every response carries rate-limit headers per bucket:

Header Meaning
X-RateLimit-Limit-{read|write} The configured threshold for that bucket.
X-RateLimit-Remaining-{read|write} Requests remaining in the current window for this customer.
X-RateLimit-Reset-{read|write} Unix timestamp (seconds) when the window resets. Use this as the "retry after" target rather than computing your own.

When a bucket is exhausted, the API returns HTTP 429 Too Many Requests with body {"status": 429, "error": ["rate_limited"]}. The X-RateLimit-Reset-* header on the 429 response tells you when to retry.

Operational note. Counters are maintained per-pod in process memory. With multiple gateway replicas the effective ceiling a single client can hit is roughly N × limit, where N is the number of replicas behind the load balancer. Treat the published limits as the minimum guaranteed ceiling, not the maximum: a well-behaved client should stay well below them so it does not depend on routing luck.

Error Handling

Branch on the HTTP status code, and read a machine-readable code from error[0]. An error carries a status, an error array, and — on every code except the rate-limit 429 — a timestamp. There is no message field. A request that fails validation is the one exception, and is shown below.

{
  "status": 403,
  "timestamp": "2026-09-10T09:14:22.815Z",
  "error": ["Forbidden resource"]
}

Where the API has a code for the condition, error[0] is that code rather than a sentence — INSUFFICIENT_CREDITS on a 402, rate_limited on a 429. Match on it exactly, including case:

{
  "status": 402,
  "timestamp": "2026-09-10T09:14:22.815Z",
  "error": ["INSUFFICIENT_CREDITS"]
}

A 400 raised by request validation is the one body that does not follow the shape above: it names each offending field separately, under errors (plural, and objects rather than strings), and carries no timestamp:

{
  "status": 400,
  "errors": [{"key": "linkedinUrls", "error": "linkedinUrls must not be empty"}]
}
Status Code Description
200 Success
400 Bad Request — invalid parameters
402 Insufficient credits — the credit balance is exhausted and the write was refused. Reads are unaffected. Top up to continue writing.
403 Forbidden — missing, unknown or revoked API key, or a resource that is not yours
404 Not Found — resource doesn't exist
412 Your own LinkedIn is not connected to Draftboard. Ratings and personal labels belong to a person, so a rating or label write, a rating / label filter, or label counts are refused instead of guessing whose to use. Nothing was read or written; connect LinkedIn in the app and retry.
429 Too Many Requests — rate limit exceeded
500 Internal Server Error
503 The credit balance could not be verified, so the write was refused rather than let through. Safe to retry.

There is no 401. An unauthenticated or revoked key is rejected with 403.