Scribble contacts API: create and list contacts
For developers
POST /api/v1/contacts creates or updates a contact, upserting on external_id so a nightly sync does not create duplicates. GET /api/v1/contacts lists the address book and filters by email or external id. An update replaces every field, so send the complete record every time.
On this page
#POST/api/v1/contacts
Create a contact, or replace one matched by external_id.
Requires scope contacts:write
Body parameters
| Field | Type | Description |
|---|---|---|
external_id | string | Your system's identifier. When present, Scribble updates the matching contact instead of creating a new one. Omit it and every call creates a duplicate. |
namerequired | string | Or first_name and last_name. Given only name, the first word becomes the first name and the rest the last name. |
first_name | string | Preferred over name. |
last_name | string | Preferred over name. |
company | string | Available as {{company}}. |
job_title | string | Available as {{job_title}}. |
email | string | Never mailed to. It is the key the do-not-mail list matches on. |
phone | string | Stored, never used. |
address | object | line1, line2, city, state, zip, country. State is upper-cased, country defaults to US. |
birthday | string | MM-DD or YYYY-MM-DD. Only the month and day are stored, whatever you send. |
work_anniversary | string | YYYY-MM-DD. The year is kept, because it is what makes {{years}} work. |
curl -X POST https://scribblecards.com/api/v1/contacts \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "crm-8812",
"first_name": "Priya",
"last_name": "Raman",
"email": "priya@northgate.example",
"company": "Northgate",
"job_title": "Head of People",
"birthday": "1990-03-14",
"work_anniversary": "2021-06-01",
"address": {
"line1": "1 Fifth Avenue",
"line2": "Apt 12B",
"city": "New York",
"state": "ny",
"zip": "10003"
}
}'{
"id": "602dba69-aa5f-4d58-9e9c-2197bd33c5e6",
"object": "contact",
"external_id": "crm-8812",
"name": "Priya Raman",
"first_name": "Priya",
"last_name": "Raman",
"company": "Northgate",
"job_title": "Head of People",
"email": "priya@northgate.example",
"phone": null,
"address": {
"line1": "1 Fifth Avenue",
"line2": "Apt 12B",
"city": "New York",
"state": "NY",
"zip": "10003",
"country": "US",
"status": "unverified"
},
"birthday": "03-14",
"work_anniversary": "2021-06-01",
"created_at": "2026-08-05T09:38:52.022303+00:00",
"updated_at": "2026-08-05T09:38:52.022303+00:00"
}201on create,200on update. The contactidis stable across updates.stateis upper-cased for you:nybecameNYin the response above.birthdaycomes back asMM-DD. The year you sent was discarded.address.statusis alwaysunverified. Scribble checks the shape of an address, not its deliverability. See address requirements.phoneis returned in contact responses, or null when no phone number is stored.- There is no matching on email, deliberately: two people who share an inbox would be silently merged, which is worse than a duplicate.
#GET/api/v1/contacts
List the address book, newest first.
Requires scope contacts:read
Query parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Default 50, maximum 200. |
email | string | Case-insensitive exact match. |
external_id | string | Exact match. This is the reconciliation query for a sync. |
curl "https://scribblecards.com/api/v1/contacts?external_id=crm-8812" \
-H "Authorization: Bearer sk_live_..."{
"object": "list",
"has_more": false,
"data": [
{
"id": "602dba69-aa5f-4d58-9e9c-2197bd33c5e6",
"object": "contact",
"external_id": "crm-8812",
"name": "Priya Raman",
"first_name": "Priya",
"last_name": "Raman",
"company": "Northgate",
"job_title": "Head of People",
"email": "priya@northgate.example",
"address": {
"line1": "1 Fifth Avenue",
"line2": "Apt 12B",
"city": "New York",
"state": "NY",
"zip": "10003",
"country": "US",
"status": "unverified"
},
"birthday": "03-14",
"work_anniversary": "2021-06-01",
"created_at": "2026-08-05T09:38:52.022303+00:00",
"updated_at": "2026-08-05T09:38:52.022303+00:00"
}
]
}#What the contacts API cannot do
- Delete.
contacts:writeexplicitly excludes deletion. Remove people from lists in the app, or add them to the do-not-mail list. - Manage lists. There is no list endpoint. Membership is managed in the app.
- Fetch a single contact by id. Use
GET /api/v1/contacts?external_id=…. - Add someone to the do-not-mail list.
For the full sync recipe including rate limiting and reconciliation, see keeping contacts in sync.
Last checked against the product on . Something wrong or missing? Tell us.
