/v1/contacts/importImport contacts
contact:createBulk import by GitHub id, login, or profile URL (≤10000 rows). Logins resolve server-side; mapped notes land as context notes; the response is the receipt. Pass shortlistId to land the contacts on an existing shortlist, or createShortlist to mint a new one for this import. Instead of rows, pass bundle to ingest a bundle from GET /v1/contacts?format=bundle, including one exported by another account: its members are re-resolved by GitHub id and a shortlist bundle recreates its list here under a freshly minted id. Every id in the bundle is provenance only and is never reused.
Request body ImportContactsRequest
fileNamestringThe name of the file this import came from, recorded on the receipt for your own traceability.
max 200 charsrowsarray of objectThe rows to import, up to 10000. Send this OR `bundle`, never both and never neither.
min 1 items, max 10000 itemsidentitystring requiredOne person, as a GitHub numeric id, a GitHub login, or a github.com profile URL. Logins and URLs are resolved server-side; anything that resolves to nothing comes back in the receipt’s `unmatched`.
max 200 charsnotestringA note to attach to this contact, landed as a context note.
max 10000 chars
bundleobjectversionnumber requiredBundle format version. Bumped only on a breaking change; an importer rejects a version it does not know rather than guessing.
1kindstring required`shortlist` = one named list and its members, and importing recreates that list. `contacts` = an arbitrary filtered selection with no list of its own.
shortlistcontactssourceobject requiredWhere this bundle came from.
accountRefstring requiredThe exporting account, as an opaque string. Provenance only: an import always writes to the account holding the credential, never to this one.
max 200 charsshortlistRefstring | null requiredThe exporting account’s shortlist id, when this is a shortlist bundle. Provenance only; a fresh id is minted on import.
max 200 charsexportedAtstring requiredWhen the bundle was exported, as an ISO 8601 instant.
max 40 chars
shortlistobject | null requiredThe list header. Present exactly when `kind` is `shortlist`, null otherwise.
namestring requiredThe list name. This IS used on import: it is the name of the list recreated in your account.
max 80 charsdescriptionstring | null requiredThe list description. Null when it had none.
max 280 charskindstring requiredWhat kind of shortlist this was in the source account.
freestandingprojectpipelinememberCountinteger requiredHow many members the source list had. Can exceed `members.length`, which is capped per bundle.
sequenceobject | null requiredThe outreach sequence attached to the source list, carried as metadata only. Importing recreates the list and its members, never the sequence.
refstring requiredThe source account’s sequence id. Provenance only.
max 200 charsnamestring requiredThe sequence name.
max 80 charsjitterMinutesinteger requiredThe random spread applied to send times, in minutes.
pausedAtstring | null requiredWhen the sequence was paused, as an ISO 8601 instant. Null when it was running.
max 40 chars
membersarray of object requiredThe contacts in this bundle, up to 5000.
max 5000 itemsrefstring requiredThe contact id in the SOURCE account. Provenance and deduplication only; it is never inserted here and means nothing in your account.
max 200 charsdeveloperIdstring requiredThe portable identity: a GitHub developer id, which means the same thing in every account. This is the ONLY field the importer resolves on.
max 200 charsdeveloperobject | null requiredThe developer’s profile as the source account saw it. Null when nothing resolved. Never carries an email address, even if the source account had bought one: an address stays with the account that paid for it.
developerIdstring requiredThe stable Vamo identifier for this developer (GitHub's numeric user id, stringified). This is the id every other endpoint takes: facets, reports, contacts, shortlists, steering. Use it, not `login` — a login can be renamed and then belongs to someone else.
max 64 charsloginstring requiredThe GitHub handle as of the last snapshot. Display it, do not key on it: handles are renameable and reusable.
max 200 charsnamestring | null requiredDisplay name as the developer set it on GitHub. Null when they set none.
max 200 charsavatarUrlstring | null requiredGitHub avatar image URL. Null when absent.
max 2048 charscurrentRolestring | null requiredA single free-text role line for the developer. Best-effort and unverified; `professional.title` is the LinkedIn-sourced equivalent when we hold a LinkedIn overlay.
max 200 charsorganizationobject | null requiredThe developer's current employer, when one is known. Null means we hold no employer, NOT that they are unemployed.
namestring requiredThe organization name as recorded.
max 200 charslogoUrlstring | null requiredOrganization logo URL, null when absent.
max 2048 chars
universitystring | null requiredMost recent institution, when the LinkedIn overlay carries education. Null when unknown.
max 200 charsprofilesobject requiredWhere this developer can be found, plus the addresses if you have already bought them.
githubstring requiredThe developer’s github.com profile URL. Always present — GitHub is the identity anchor for every developer in the index.
max 2048 charslinkedinstring | null requiredLinkedIn profile URL when we hold a GitHub↔LinkedIn match for this person. Null for the large majority of developers: only a minority of the indexed GitHub population carries a LinkedIn overlay, so null means "no match held", never "no LinkedIn account".
max 2048 charsemailsarray of stringEvery email address we hold for this developer. Present ONLY when this account has already paid to reveal them and the purchase is still within its cache window; a base search or profile read never populates it. ABSENT is not the same as "no email": absent means you have not bought it, while `hasEmail: false` means we hold none. These are addresses we have OBSERVED — from public commit metadata and cached GitHub profile data — and we do not check deliverability, so treat the order as meaningless and expect some to bounce. Buy them with the `contact.emails` facet.
max 50 items
hasEmailboolean requiredWhether we hold at least one email address for this developer. It says we have an address on file, not that the address works — we do not check deliverability. The addresses themselves are never returned by a search or a base profile read: this boolean is the free signal, and the paid `contact.emails` facet is the only way to get them. Use `hasEmail` on a search request to filter to developers we hold an address for, without paying.
biostring | null requiredThe developer’s GitHub bio, verbatim. Null when empty.
max 5000 charslocationobject | null requiredSelf-reported location, plus our geocoding of it. Self-reported and never verified against anything: a developer can leave it stale or blank. Null when we hold nothing at all.
rawstring | null requiredThe location string exactly as the developer typed it on GitHub ("SF / remote").
max 200 charscitystring | null requiredCity resolved from `raw` by geocoding. Null when `raw` was empty or unresolvable.
max 200 charscountrystring | null requiredCountry resolved from `raw`. Null when `raw` was empty or unresolvable.
max 200 chars
joinedAtstring | null requiredWhen the GitHub account was created, as an ISO 8601 instant. Null when unknown.
max 40 charsstatsobject | null requiredSnapshot counts and standing for this developer. These are from our index and go stale between refreshes; `GET /v1/developers/{id}` returns live GitHub equivalents alongside the contribution garden when you need current numbers. Null when we hold no stats at all.
followersinteger | null requiredGitHub follower count at snapshot time.
min 0, max 100000000followinginteger | null requiredHow many accounts this developer follows, at snapshot time.
min 0, max 100000000totalStarsinteger | null requiredSum of stargazers across the repositories ATTRIBUTED to this developer — the same set as `repos`, so the count can never contradict the list. Owned and contributed-to repositories both count; forks do not.
min 0, max 100000000crackedScorenumber | null requiredDevrank: a 0–100 standing for this developer within the WHOLE indexed GitHub developer population (tens of millions of accounts), not within a language, region, or seniority peer group — so it is not a like-for-like comparison between two candidates for the same role. It is derived from the follower/contribution graph, is recomputed upstream on its own schedule, and carries no version on the wire, so the same developer can read differently across two snapshots. It contributes one term to the page-local `score` and backs the `levers.github.minDevrank` filter. Null when the developer is not in the devrank table.
tierstring | null requiredThe upstream provider’s own coarse label for `crackedScore` (for example "Elite"). The label set is the provider’s and is not a Vamo-stable vocabulary — treat it as display text and branch on `crackedScore` if you need a threshold. Null when the developer is not in the devrank table.
max 200 chars
reposarray of object requiredThe developer’s top ATTRIBUTED public repositories — owned or authored-into, forks excluded — stars descending, capped at 12. This is a sample of their best work, not a complete repository list, and it is not an answer to "why did this person match my query" — read `evidence.matchedRepos` for that.
max 12 itemsnamestring requiredThe repository name alone, without the owner (`linux`).
max 200 charsfullNamestring requiredGitHub `owner/name`. This is the join key: an evidence repo and a profile repo describing the same repository carry the same `fullName`.
max 200 charsdescriptionstring | null requiredThe repository's own GitHub description, verbatim. Null when it has none.
max 5000 charslanguagestring | null requiredGitHub's primary language for the repository. Null when GitHub reports none (an empty or docs-only repo); never inferred from the code.
max 200 charsstarsinteger requiredStargazer count at the time this profile was snapshotted, not at request time.
min 0, max 100000000commitsintegerHow many commits THIS developer authored to this repository over the trailing year. This is the difference between a project they wrote and a project they landed one drive-by fix in — a 385k-star repository with 4 of their commits is not their work. Absent when no commit signal was captured; 0 means none in the trailing year, which for older dormant work is not the same as none ever.
min 0, max 100000000ownedbooleanTrue when the repository sits under the developer's own account, false when it is someone else's (an organisation's or another person's) and they are a contributor to it. This is the difference between "they built this" and "they committed to this", and it is the ONLY honest basis for crediting a repository's stars to a person. Absent when no ownership signal was captured; treat absent as unknown, never as false.
urlstring requiredThe public github.com URL of the repository.
max 2048 chars
languagesarray of string requiredDistinct primary languages across the developer’s repositories, at most 8. Presence means they have shipped public code in it; the list is not ranked by proficiency and carries no line counts.
max 8 itemsexperiencearray of object requiredEmployment history from the LinkedIn overlay, newest first. Empty for any developer we hold no LinkedIn match for, which is most of them.
max 25 itemstitlestring | nullJob title, on an experience entry.
max 200 charscompanystring | nullEmployer name, on an experience entry.
max 200 charsschoolstring | nullInstitution name, on an education entry.
max 200 charsdegreestring | nullDegree awarded, on an education entry.
max 200 charsfieldOfStudystring | nullField of study, on an education entry.
max 200 charsstartDatestring | nullStart date exactly as the source rendered it. Free text, NOT a normalised date: expect "2019", "Jan 2019" and "2019-01" from different records. Parse defensively.
max 200 charsendDatestring | nullEnd date in the same unnormalised free-text form as `startDate`. Absent or null on a current role.
max 200 charscurrentboolean | nullThe source's own "still there" flag. Set on experience entries; null on education entries.
educationarray of object requiredEducation history from the LinkedIn overlay. Empty for any developer we hold no LinkedIn match for.
max 25 itemstitlestring | nullJob title, on an experience entry.
max 200 charscompanystring | nullEmployer name, on an experience entry.
max 200 charsschoolstring | nullInstitution name, on an education entry.
max 200 charsdegreestring | nullDegree awarded, on an education entry.
max 200 charsfieldOfStudystring | nullField of study, on an education entry.
max 200 charsstartDatestring | nullStart date exactly as the source rendered it. Free text, NOT a normalised date: expect "2019", "Jan 2019" and "2019-01" from different records. Parse defensively.
max 200 charsendDatestring | nullEnd date in the same unnormalised free-text form as `startDate`. Absent or null on a current role.
max 200 charscurrentboolean | nullThe source's own "still there" flag. Set on experience entries; null on education entries.
professionalobject | nullLinkedIn-sourced professional scalars, present only when we hold a LinkedIn match for this developer. Absent or null for the majority of developers, which says nothing about their employment — only that we hold no overlay.
headlinestring | null requiredThe LinkedIn headline, verbatim.
max 5000 charstitlestring | null requiredCurrent job title per LinkedIn, e.g. "Chief Technology Officer".
max 200 charscompanystring | null requiredCurrent employer per LinkedIn. Distinct from the GitHub-sourced `organization` field, and the two disagree often — LinkedIn is usually the fresher of the two.
max 200 charssenioritystring | null requiredA coarse seniority label from the LinkedIn overlay ("c-level", "senior", …). Free text from the source, not a Vamo enum; do not switch on the exact string.
max 200 charsexpertisearray of string requiredSelf-declared skill tags from the LinkedIn profile. Claimed, not demonstrated — the demonstrated equivalent is the repositories under `repos` and `evidence`.
max 25 items
provenanceobject requiredHow the contact arose in the source account.
sourcestring requiredHow this contact entered the SOURCE account.
searchfeedimportmanualapilookupown-companycompetitorsourceDetailstring | null requiredFree-text detail behind `source`. Null when there was none.
max 1000 charsaddedAtstring requiredWhen they were added in the source account, as an ISO 8601 instant.
max 40 charsaddedByNamestring | null requiredThe display name of whoever added them in the source account. Deliberately a name and never an id or an email address.
max 200 charsdoNotContactboolean requiredWhether they were marked do-not-contact in the source account.
hiddenboolean requiredWhether they were hidden in the source account.
engagementobject requiredThe source account’s outreach history with them, for context only.
firstContactedAtstring | nullWhen the source account first contacted them. Null means never; ABSENT means withheld.
max 40 charslastContactedAtstring | nullWhen the source account last contacted them. Null means never; absent means withheld.
max 40 charscontactedCountintegerHow many times the source account contacted them. Absent means withheld.
rolesCountinteger requiredHow many roles they were put forward for in the source account.
activeSequencesintegerHow many live sequences they were in. Absent means withheld.
sequenceNamesarray of stringNames of those sequences. Absent means withheld.
max 100 itemslistNamesarray of string requiredThe source account’s list names this contact belonged to. Labels for context, not ids and not instructions.
max 100 items
shortlistIdstringLand the imported contacts on this existing shortlist. Send this OR `createShortlist`.
max 16 charscreateShortliststringMint a new shortlist with this name and land the imported contacts on it. Send this OR `shortlistId`.
max 200 chars
Responses
200The import receiptidstring requiredThe receipt id for this import.
rowCountinteger requiredHow many rows were submitted.
createdinteger requiredHow many contacts were newly created.
restoredinteger requiredHow many previously removed contacts were brought back.
existinginteger requiredHow many rows matched a contact you already had, and so changed nothing.
unmatchedarray of string requiredThe input identities that resolved to no developer, echoed back. These are the rows to fix and resubmit; they were not imported.
shortlistobjectThe shortlist the imported contacts landed on, when one was named or created. Absent when they were imported without a list.
idstring requiredThe shortlist id, freshly minted in YOUR account.
namestring requiredThe shortlist name.
400Empty, too many rows, or note too longcodestring requiredStable machine-readable error code. Branch on this, never on the numeric status.
bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errormessagestring requiredHuman-readable explanation of the refusal.
statusinteger requiredThe HTTP status code, repeated in the body.
remedyobjectA self-serve path forward, when one exists (a 402 points at the credit top-up).
kindstring requiredWhat kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.
topupconnect_mailboxurlstring requiredWhere to go to clear the condition: an API path, or a web app page when only a person can.
401No or invalid credentialcodestring requiredStable machine-readable error code. Branch on this, never on the numeric status.
bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errormessagestring requiredHuman-readable explanation of the refusal.
statusinteger requiredThe HTTP status code, repeated in the body.
remedyobjectA self-serve path forward, when one exists (a 402 points at the credit top-up).
kindstring requiredWhat kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.
topupconnect_mailboxurlstring requiredWhere to go to clear the condition: an API path, or a web app page when only a person can.
403Missing contact:createcodestring requiredStable machine-readable error code. Branch on this, never on the numeric status.
bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errormessagestring requiredHuman-readable explanation of the refusal.
statusinteger requiredThe HTTP status code, repeated in the body.
remedyobjectA self-serve path forward, when one exists (a 402 points at the credit top-up).
kindstring requiredWhat kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.
topupconnect_mailboxurlstring requiredWhere to go to clear the condition: an API path, or a web app page when only a person can.