Browse documentation

Type to search the documentation. Press Esc to close.

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

FieldTypeDescription
external_idstringYour system's identifier. When present, Scribble updates the matching contact instead of creating a new one. Omit it and every call creates a duplicate.
namerequiredstringOr first_name and last_name. Given only name, the first word becomes the first name and the rest the last name.
first_namestringPreferred over name.
last_namestringPreferred over name.
companystringAvailable as {{company}}.
job_titlestringAvailable as {{job_title}}.
emailstringNever mailed to. It is the key the do-not-mail list matches on.
phonestringStored, never used.
addressobjectline1, line2, city, state, zip, country. State is upper-cased, country defaults to US.
birthdaystringMM-DD or YYYY-MM-DD. Only the month and day are stored, whatever you send.
work_anniversarystringYYYY-MM-DD. The year is kept, because it is what makes {{years}} work.
Upsert a contact
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"
    }
  }'
201 Created
{
  "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"
}
  • 201 on create, 200 on update. The contact id is stable across updates.
  • state is upper-cased for you: ny became NY in the response above.
  • birthday comes back as MM-DD. The year you sent was discarded.
  • address.status is always unverified. Scribble checks the shape of an address, not its deliverability. See address requirements.
  • phone is 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

ParameterTypeDescription
limitintegerDefault 50, maximum 200.
emailstringCase-insensitive exact match.
external_idstringExact match. This is the reconciliation query for a sync.
Read one contact back by your own id
curl "https://scribblecards.com/api/v1/contacts?external_id=crm-8812" \
  -H "Authorization: Bearer sk_live_..."
200 OK
{
  "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:write explicitly 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.