curl --request POST \
--url https://app.pipecorn.com/api/v2/contacts/single_enrich \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"firstname": "Mathieu",
"lastname": "Brun-Picard",
"domain": "pipecorn.com",
"company_name": "Pipecorn",
"linkedin_url": "https://www.linkedin.com/in/mathieu-brun-picard/",
"webhook_url": "https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8",
"enrichment_type": [
"phone"
],
"phone_type": [
"mobile"
],
"custom": {
"hubspot_id": "4447901"
}
}
'import requests
url = "https://app.pipecorn.com/api/v2/contacts/single_enrich"
payload = {
"firstname": "Mathieu",
"lastname": "Brun-Picard",
"domain": "pipecorn.com",
"company_name": "Pipecorn",
"linkedin_url": "https://www.linkedin.com/in/mathieu-brun-picard/",
"webhook_url": "https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8",
"enrichment_type": ["phone"],
"phone_type": ["mobile"],
"custom": { "hubspot_id": "4447901" }
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
firstname: 'Mathieu',
lastname: 'Brun-Picard',
domain: 'pipecorn.com',
company_name: 'Pipecorn',
linkedin_url: 'https://www.linkedin.com/in/mathieu-brun-picard/',
webhook_url: 'https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8',
enrichment_type: ['phone'],
phone_type: ['mobile'],
custom: {hubspot_id: '4447901'}
})
};
fetch('https://app.pipecorn.com/api/v2/contacts/single_enrich', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.pipecorn.com/api/v2/contacts/single_enrich",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'firstname' => 'Mathieu',
'lastname' => 'Brun-Picard',
'domain' => 'pipecorn.com',
'company_name' => 'Pipecorn',
'linkedin_url' => 'https://www.linkedin.com/in/mathieu-brun-picard/',
'webhook_url' => 'https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8',
'enrichment_type' => [
'phone'
],
'phone_type' => [
'mobile'
],
'custom' => [
'hubspot_id' => '4447901'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.pipecorn.com/api/v2/contacts/single_enrich"
payload := strings.NewReader("{\n \"firstname\": \"Mathieu\",\n \"lastname\": \"Brun-Picard\",\n \"domain\": \"pipecorn.com\",\n \"company_name\": \"Pipecorn\",\n \"linkedin_url\": \"https://www.linkedin.com/in/mathieu-brun-picard/\",\n \"webhook_url\": \"https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8\",\n \"enrichment_type\": [\n \"phone\"\n ],\n \"phone_type\": [\n \"mobile\"\n ],\n \"custom\": {\n \"hubspot_id\": \"4447901\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.pipecorn.com/api/v2/contacts/single_enrich")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"firstname\": \"Mathieu\",\n \"lastname\": \"Brun-Picard\",\n \"domain\": \"pipecorn.com\",\n \"company_name\": \"Pipecorn\",\n \"linkedin_url\": \"https://www.linkedin.com/in/mathieu-brun-picard/\",\n \"webhook_url\": \"https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8\",\n \"enrichment_type\": [\n \"phone\"\n ],\n \"phone_type\": [\n \"mobile\"\n ],\n \"custom\": {\n \"hubspot_id\": \"4447901\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.pipecorn.com/api/v2/contacts/single_enrich")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"firstname\": \"Mathieu\",\n \"lastname\": \"Brun-Picard\",\n \"domain\": \"pipecorn.com\",\n \"company_name\": \"Pipecorn\",\n \"linkedin_url\": \"https://www.linkedin.com/in/mathieu-brun-picard/\",\n \"webhook_url\": \"https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8\",\n \"enrichment_type\": [\n \"phone\"\n ],\n \"phone_type\": [\n \"mobile\"\n ],\n \"custom\": {\n \"hubspot_id\": \"4447901\"\n }\n}"
response = http.request(request)
puts response.read_body{
"enrichment_id": "123e4567-e89b-12d3-a456-426614174000",
"first_name": "Mathieu",
"last_name": "Brun-Picard",
"status": "pending",
"custom": {
"hubspot_id": "4447901"
}
}{
"error": "Not enough enrichment credits left",
"code": "insufficient_credits",
"top_up_url": "https://app.pipecorn.com/settings/subscriptions/credits"
}{
"error": "You have reached your weekly enrichment credit limit for this workspace",
"code": "workspace_limit_exceeded"
}{
"error": 422,
"message": "You can't use the personal email enrichment feature without consenting with our terms of use"
}Asynchronous Single Contact Enrichment
Enrich a single contact record with email, phone, and / or personal email data
curl --request POST \
--url https://app.pipecorn.com/api/v2/contacts/single_enrich \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"firstname": "Mathieu",
"lastname": "Brun-Picard",
"domain": "pipecorn.com",
"company_name": "Pipecorn",
"linkedin_url": "https://www.linkedin.com/in/mathieu-brun-picard/",
"webhook_url": "https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8",
"enrichment_type": [
"phone"
],
"phone_type": [
"mobile"
],
"custom": {
"hubspot_id": "4447901"
}
}
'import requests
url = "https://app.pipecorn.com/api/v2/contacts/single_enrich"
payload = {
"firstname": "Mathieu",
"lastname": "Brun-Picard",
"domain": "pipecorn.com",
"company_name": "Pipecorn",
"linkedin_url": "https://www.linkedin.com/in/mathieu-brun-picard/",
"webhook_url": "https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8",
"enrichment_type": ["phone"],
"phone_type": ["mobile"],
"custom": { "hubspot_id": "4447901" }
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
firstname: 'Mathieu',
lastname: 'Brun-Picard',
domain: 'pipecorn.com',
company_name: 'Pipecorn',
linkedin_url: 'https://www.linkedin.com/in/mathieu-brun-picard/',
webhook_url: 'https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8',
enrichment_type: ['phone'],
phone_type: ['mobile'],
custom: {hubspot_id: '4447901'}
})
};
fetch('https://app.pipecorn.com/api/v2/contacts/single_enrich', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.pipecorn.com/api/v2/contacts/single_enrich",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'firstname' => 'Mathieu',
'lastname' => 'Brun-Picard',
'domain' => 'pipecorn.com',
'company_name' => 'Pipecorn',
'linkedin_url' => 'https://www.linkedin.com/in/mathieu-brun-picard/',
'webhook_url' => 'https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8',
'enrichment_type' => [
'phone'
],
'phone_type' => [
'mobile'
],
'custom' => [
'hubspot_id' => '4447901'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.pipecorn.com/api/v2/contacts/single_enrich"
payload := strings.NewReader("{\n \"firstname\": \"Mathieu\",\n \"lastname\": \"Brun-Picard\",\n \"domain\": \"pipecorn.com\",\n \"company_name\": \"Pipecorn\",\n \"linkedin_url\": \"https://www.linkedin.com/in/mathieu-brun-picard/\",\n \"webhook_url\": \"https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8\",\n \"enrichment_type\": [\n \"phone\"\n ],\n \"phone_type\": [\n \"mobile\"\n ],\n \"custom\": {\n \"hubspot_id\": \"4447901\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.pipecorn.com/api/v2/contacts/single_enrich")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"firstname\": \"Mathieu\",\n \"lastname\": \"Brun-Picard\",\n \"domain\": \"pipecorn.com\",\n \"company_name\": \"Pipecorn\",\n \"linkedin_url\": \"https://www.linkedin.com/in/mathieu-brun-picard/\",\n \"webhook_url\": \"https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8\",\n \"enrichment_type\": [\n \"phone\"\n ],\n \"phone_type\": [\n \"mobile\"\n ],\n \"custom\": {\n \"hubspot_id\": \"4447901\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.pipecorn.com/api/v2/contacts/single_enrich")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"firstname\": \"Mathieu\",\n \"lastname\": \"Brun-Picard\",\n \"domain\": \"pipecorn.com\",\n \"company_name\": \"Pipecorn\",\n \"linkedin_url\": \"https://www.linkedin.com/in/mathieu-brun-picard/\",\n \"webhook_url\": \"https://webhook.site/8b53c96f-f2cd-4c24-bc09-b3c2176d7ea8\",\n \"enrichment_type\": [\n \"phone\"\n ],\n \"phone_type\": [\n \"mobile\"\n ],\n \"custom\": {\n \"hubspot_id\": \"4447901\"\n }\n}"
response = http.request(request)
puts response.read_body{
"enrichment_id": "123e4567-e89b-12d3-a456-426614174000",
"first_name": "Mathieu",
"last_name": "Brun-Picard",
"status": "pending",
"custom": {
"hubspot_id": "4447901"
}
}{
"error": "Not enough enrichment credits left",
"code": "insufficient_credits",
"top_up_url": "https://app.pipecorn.com/settings/subscriptions/credits"
}{
"error": "You have reached your weekly enrichment credit limit for this workspace",
"code": "workspace_limit_exceeded"
}{
"error": 422,
"message": "You can't use the personal email enrichment feature without consenting with our terms of use"
}enrichment_id and status: "pending"; the waterfall
runs asynchronously in the background.
When to use this endpoint
Use the async endpoint when:- You can accept results via webhook, or you can poll for them later.
- You’re enriching at scale and don’t want to hold an HTTP connection per contact.
- You want to retain the enrichment result on the contact record for later retrieval via
GET /contacts/{id}.
Enrichment Types
Passenrichment_type as an array containing one of:
["email"]— Find and validate the professional email["phone"]— Find phone numbers (requireslinkedin_url)["personal_email"]— Find a personal email (requireslinkedin_urland account-level consent — see below)
personal_email requires the authenticated user to have
consented to personal email enrichment in their account settings.
If consent is missing, the API responds with 422 Unprocessable Entity
and the message "You can't use the personal email enrichment feature without consenting with our terms of use". This consent gate is required
for GDPR compliance.Required Fields
enrichment_type is always required. The other required fields depend on its
value (the Body section below shows this per type):
enrichment_type | Additional required fields |
|---|---|
["email"] | firstname, lastname, and one of linkedin_url, domain, or company_name |
["phone"] | linkedin_url |
["personal_email"] | linkedin_url |
linkedin_url on its own is enough, or company context (domain or
company_name). Phone and personal email enrichment run off the linkedin_url
alone, so firstname/lastname are not required for them.
Phone type filter
Whenenrichment_type includes phone, pass phone_type to choose which
numbers come back:
phone_type | Returned numbers |
|---|---|
["all"] (default) | Every number found, whatever its line type |
["mobile"] | Only mobile numbers — type of mobile or fixed_or_mobile |
["mobile"], the waterfall keeps querying providers until one returns a
mobile number — a provider that only has a landline doesn’t stop the search.
Landlines, VoIP, toll-free and other non-mobile numbers are
dropped from both phone and phone_details. If only non-mobile numbers were
found, phone is an empty array. phone_type combines with
phone_country_codes (both filters apply). Any other value is rejected with
422 Unprocessable Entity.
{
"linkedin_url": "https://www.linkedin.com/in/mathieu-brun-picard/",
"enrichment_type": ["phone"],
"phone_type": ["mobile"],
"webhook_url": "https://example.com/webhooks/pipecorn"
}
Phone types
Results onGET /contacts/{id}
and on the webhook callback list every number in phone_details with its line type:
"phone_details": [
{ "number": "+33612345678", "type": "mobile" },
{ "number": "+33145678901", "type": "fixed_line" }
]
phone keeps returning the plain list of E.164 numbers for existing
integrations; phone_details holds the same numbers in the same order.
type | Meaning |
|---|---|
mobile | Mobile number |
fixed_or_mobile | Can’t be told apart from the number alone — mobile and landline share the same ranges (e.g. US, Canada). Treated as mobile by the phone_type filter |
fixed_line | Landline (office switchboard, desk phone) |
voip | VoIP number |
toll_free | Toll-free number |
premium_rate | Premium-rate number |
shared_cost | Shared-cost number |
personal_number | Personal (follow-me) number |
pager | Pager |
uan | Universal access number (company-wide number) |
voicemail | Voicemail access number |
unknown | The number could not be classified |
Receiving results
You have two options:- Webhook — pass
webhook_urlin the request. We POST the final result to that URL once the waterfall completes. - Polling — call
GET /contacts/{id}with the returnedenrichment_id. While the waterfall is running thestatusispending; once it has completed (with or without data) thestatusflips tofinished.
Custom Fields
Passcustom: { ... } in the request to attach arbitrary correlation data
(e.g. your CRM record ID). The same payload is echoed back on
GET /contacts/{id} and on the webhook callback.
Pricing
Each enrichment consumes credits based on the requestedenrichment_type and
the providers used. A phone enrichment is only charged when a mobile number
(mobile or fixed_or_mobile) is found. Check your account credits via the
Account Credits endpoint.
Credits are checked before the contact is enqueued. If your balance cannot cover
the enrichment the request is rejected with 402 Payment Required and
code: "insufficient_credits" (the response carries a top_up_url); if it
would exceed your workspace weekly credit limit, with 403 Forbidden and
code: "workspace_limit_exceeded". Nothing is enqueued in either case. See
Credit and limit errors.Authorizations
Your API key
Body
Contact information for enrichment
- Email enrichment
- Email enrichment
- Email enrichment
- Phone enrichment
- Personal email enrichment
Requires firstname, lastname, and at least one identifier: linkedin_url, domain, or company_name.
Set to ["email"] to find and validate the professional email.
1email First name of the contact.
Last name of the contact.
LinkedIn profile URL of the contact.
"https://www.linkedin.com/in/mathieu-brun-picard/"
Company domain.
"pipecorn.com"
Company name.
"Pipecorn"
Webhook URL to receive enrichment results
Optional. Restrict phone enrichment to phone numbers from these countries. When omitted, the user's account-level default phone country codes are applied.
ISO 3166-1 alpha-2 country code (e.g. "US", "FR", "GB")
Optional. Filters the phone numbers returned by phone enrichment. ["all"] (default) returns every number found; ["mobile"] returns only mobile numbers (type of mobile or fixed_or_mobile) and drops landlines, VoIP, toll-free and other non-mobile numbers. With ["mobile"] the waterfall keeps querying providers until one returns a mobile number. Any other value is rejected with 422. Ignored unless enrichment_type includes phone.
1all, mobile Custom fields to include with the enrichment that will be returned in the webhook (like crm id, etc.)
Response
Enrichment request accepted. The waterfall runs asynchronously; poll GET /contacts/{id} (using the returned enrichment_id) or wait for the webhook_url callback to retrieve final results.
The ID of the enrichment request. Use it to poll GET /contacts/{id}.
The contact's first name
The contact's last name
Always pending on creation; the waterfall runs asynchronously.
pending Custom fields passed in the request
Was this page helpful?