Clay setup guide
| Status | Kind | How you connect |
|---|---|---|
| Coming soon | Outreach | The 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_statusand a realconsent_sourceonly for people who agreed to texts. - In Clay, save the key as an HTTP API (Headers) account (header
Authorization, valueBearerand your key), so it isn't shown in plain text to everyone who can see the table.
Setup
With the bubbl3 API
- Find the contact. Add an HTTP API column: method
GET, URLhttps://api.bubbl3.com/v1/contacts?phone=followed by the row's phone column, and your saved header account. The answer'sdatalist has the contact if bubbl3 already has it. - Add or update it. Add a second HTTP API column that runs only when the first found nobody: method
POST, URLhttps://api.bubbl3.com/v1/contacts, body withphone,first_name,last_name,emailandlabels(for example["clay"]), andconsent_statuswithconsent_sourceonly when you have the person's consent. To update a contact bubbl3 already has, usePATCHonhttps://api.bubbl3.com/v1/contacts/{id}with itsid. Add anIdempotency-Keyheader made from the row's id, such asclay-and the row id, so a rerun of the column doesn't create the contact twice. - Check before you text. Add a column with
POSTtohttps://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). - Send. Add a last column that runs only when the check didn't say
reject:POSTtohttps://api.bubbl3.com/v1/messageswith the same body and anIdempotency-Keymade from the row's id and the message, such asclay-and the row id and-intro. A rerun with the same key returns the first message instead of sending again. - 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_statuswith itsconsent_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.
| Reason | Code | What you see |
|---|---|---|
| A link before the contact replies | pre_reply_link_blocked | Not sent: "Links can't be sent until the contact replies" |
| A photo or file before the contact replies | pre_reply_media_blocked | Not sent: "Photos and files can't be sent until the contact replies" |
| A phone number before the contact replies | pre_reply_phone_blocked | Not sent: "Phone numbers can't be sent until the contact replies" |
| An email address before the contact replies | pre_reply_email_blocked | Not sent: "Email addresses can't be sent until the contact replies" |
| A first message that is too long | pre_reply_too_long | Not sent: "First messages must be 300 characters or fewer" |
| Too many texts without a reply | pre_reply_message_limit | Not sent: "Already sent 3 texts without a reply. Wait for the contact to reply" |
| The same text to too many people today | identical_text_limit | Not sent: "This exact text went to 20 people from this line today" |
| The line is paused after unanswered texts | unanswered_streak_limit | Not sent: "This line is paused after too many unanswered texts" |
| The contact opted out | contact_opted_out | Not sent: "The contact opted out of texts" |
| The workspace texts only opted-in contacts | consent_required | Not sent: "This workspace only texts contacts who opted in" |
| No line can reach the contact | line_unavailable | Not sent: "bubbl3's line for this contact isn't available" |
| Quiet hours | quiet_hours | Held: "Scheduled for 9:00 AM America/Chicago, quiet hours" |
| The line's daily limit | daily_new_contact_cap | Held: "Scheduled for Oct 15, 9:00 AM America/Chicago: today's limit for this line is reached" |
| The line's hourly limit of new contacts | hourly_new_contact_cap | Held: "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
429answer 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.statuswebhook 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
401in the column. The header account's value must beBearerand the key, with a space between.403on create or update. The key needs the admin scope to change contacts.- The check says
reject. Itsreasonsays 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
defergives the time: quiet hours in the contact's time zone, or the line's limit for the day.