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",
"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 accepted but is not returned.- 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.