Skip to main content

Clay setup guide

StatusKindHow you connect
Coming soonOutreachThe bubbl3 API with your key

This guide is coming soon: bubbl3 is still checking it end to end. It shows how Clay will send leads to bubbl3, with nothing to install.

What it does​

Clay's HTTP API column calls any API from a table, one row at a time. With it, each row in a Clay table can become a bubbl3 contact (found by phone number and updated, or created) and, if you choose, get a text from bubbl3. There's no app to install: Clay calls the bubbl3 API with an API key you make for it.

Clay rows usually come from outbound lists, so bubbl3 treats these contacts as cold: see the sending rules below.

Before you start​

  • A bubbl3 API key just for Clay, made in Developers, API keys. The read and send scopes cover finding contacts and checking and sending texts; add admin only to create or update contacts. Use a test key (b3_test_...) while you build: texts to test numbers only reach bubbl3's device simulator.
  • A Clay table with a phone number column, in E.164 (+19175550142) or a US or Canada format bubbl3 can read.
  • Decide how these contacts' consent is handled. Contacts Clay adds through the API keep the consent you send (unknown if you send none), and one whose consent is unknown can be texted unless the workspace is set to text only opted-in contacts. For outbound lists, turn that setting on, or send consent_status and a real consent_source only for people who agreed to texts.
  • In Clay, save the key as an HTTP API (Headers) account (header Authorization, value Bearer and your key), so it isn't shown in plain text to everyone who can see the table.

Setup​

With the bubbl3 API​

  1. Find the contact. Add an HTTP API column: method GET, URL https://api.bubbl3.com/v1/contacts?phone= followed by the row's phone column, and your saved header account. The answer's data list has the contact if bubbl3 already has it.
  2. Add or update it. Add a second HTTP API column that runs only when the first found nobody: method POST, URL https://api.bubbl3.com/v1/contacts, body with phone, first_name, last_name, email and labels (for example ["clay"]), and consent_status with consent_source only when you have the person's consent. To update a contact bubbl3 already has, use PATCH on https://api.bubbl3.com/v1/contacts/{id} with its id. Add an Idempotency-Key header made from the row's id, such as clay- and the row id, so a rerun of the column doesn't create the contact twice.
  3. Check before you text. Add a column with POST to https://api.bubbl3.com/v1/messages/check, body {"to": <phone>, "body": <your text>}. Its answer says, for each recipient, whether bubbl3 would send now (allow), hold the text (defer, with the time and reason) or refuse it (reject, with the reason).
  4. Send. Add a last column that runs only when the check didn't say reject: POST to https://api.bubbl3.com/v1/messages with the same body and an Idempotency-Key made from the row's id and the message, such as clay- and the row id and -intro. A rerun with the same key returns the first message instead of sending again.
  5. In each column's settings, set rate limiting to a few requests a second and run it on one row first.

bubbl3 decides when each text goes out: a text that's held for quiet hours or a line's limit waits in bubbl3 and goes out later, so don't resend it from Clay.

Data shared​

From Clay to bubbl3: the fields you put in each column's body: phone, name, email, labels and the text to send.

From bubbl3 to Clay: each call's answer: the contact's id and fields, the check's decision and reason, and the message's id and status.

Sending rules that still apply​

Clay never texts anyone itself. Its contacts come from outbound lists: they haven't asked to hear from you, which makes them the riskiest texts for a line's health. Every text to them goes through the same checks as a text you send from bubbl3, and nothing in Clay can turn them off.

  • Cold contacts. Contacts that arrive from Clay are cold contacts. They share each line's normal limit for new contacts below, like everyone else, unless your workspace sets a lower daily limit for cold contacts in compliance settings. When you add or update a contact through the bubbl3 API, send "outreach_class": "cold" to mark it. See cold outreach.
  • Consent. bubbl3 never texts a contact who opted out. Contacts Clay adds through the bubbl3 API keep the consent you give them: one whose consent is unknown can be texted, unless the workspace is set to text only opted-in contacts. Record each contact's real consent (consent_status with its consent_source) when you have it.
  • Opt-out. Replying STOP opts a contact out at once.
  • Quiet hours. No texts from 8:00 PM to 9:00 AM in the contact's own time zone (from the phone's area code). A text due then waits until the morning.
  • Pacing. Each line texts at most 15 new contacts an hour and 50 a day (fewer while a new line warms up), and spaces texts 20 to 90 seconds apart. A big import or batch is queued and sent as the limits allow, never dropped.
  • First messages. Until a contact replies, a text can't include links, photos or files, phone numbers or email addresses, must be 300 characters or fewer, and at most 3 texts go out.

What Clay is told when bubbl3 doesn't send​

Clay sees bubbl3's answer in the column's result. POST /v1/messages/check answers each recipient with allow, defer or reject and the reason in plain words, and a refused send answers with an error code and message.

ReasonCodeWhat you see
A link before the contact repliespre_reply_link_blockedNot sent: "Links can't be sent until the contact replies"
A photo or file before the contact repliespre_reply_media_blockedNot sent: "Photos and files can't be sent until the contact replies"
A phone number before the contact repliespre_reply_phone_blockedNot sent: "Phone numbers can't be sent until the contact replies"
An email address before the contact repliespre_reply_email_blockedNot sent: "Email addresses can't be sent until the contact replies"
A first message that is too longpre_reply_too_longNot sent: "First messages must be 300 characters or fewer"
Too many texts without a replypre_reply_message_limitNot sent: "Already sent 3 texts without a reply. Wait for the contact to reply"
The same text to too many people todayidentical_text_limitNot sent: "This exact text went to 20 people from this line today"
The line is paused after unanswered textsunanswered_streak_limitNot sent: "This line is paused after too many unanswered texts"
The contact opted outcontact_opted_outNot sent: "The contact opted out of texts"
The workspace texts only opted-in contactsconsent_requiredNot sent: "This workspace only texts contacts who opted in"
No line can reach the contactline_unavailableNot sent: "bubbl3's line for this contact isn't available"
Quiet hoursquiet_hoursHeld: "Scheduled for 9:00 AM America/Chicago, quiet hours"
The line's daily limitdaily_new_contact_capHeld: "Scheduled for Oct 15, 9:00 AM America/Chicago: today's limit for this line is reached"
The line's hourly limit of new contactshourly_new_contact_capHeld: "Scheduled for 2:00 PM America/Chicago: this hour's limit of new contacts for this line is reached"

The numbers are the default limits; a line's own limits change them. bubbl3's other checks (no phone number, a number that doesn't match) have their own plain reasons.

Limits and known gaps​

  • Clay calls bubbl3 once per row and column, and each call counts against your API key's rate limit. A 429 answer says how long to wait.
  • bubbl3 won't text a contact who opted out, whatever the column sends, and sends to cold contacts within each line's normal daily limit for new contacts. A big table is sent over days, not at once.
  • Clay doesn't hear back when a held text is finally sent. Use a message.status webhook in bubbl3's Developers, Webhooks if you need that.
  • Credentials typed into a column's Headers field are visible to anyone who can see the table; use a saved header account.

Troubleshooting​

  • 401 in the column. The header account's value must be Bearer and the key, with a space between.
  • 403 on create or update. The key needs the admin scope to change contacts.
  • The check says reject. Its reason says why in plain words, for example a link in a first message or a contact who opted out. Change the text or leave the row.
  • Texts are held. The check's defer gives the time: quiet hours in the contact's time zone, or the line's limit for the day.