Skip to main content
Embedded forms let any website collect subscribers into Senderz. You do not need a connected store. Paste a script tag and a form into your page, and every submission creates or updates a contact, records the opt-in, and fires your subscribe automations.
This connector is live end to end. The script, the subscribe endpoint, the allowed-websites control and the settings screen all ship today. The one thing the snippet cannot do is collect a phone number on its own, without an email address. See Phone-only forms.

Before you start

You need an API key. The form carries it in the page source, so it is a public value by design, and the only thing it can do is subscribe someone.
1

Create an API key

Go to Settings → API Tokens and select Create key. Give it a name such as Website form, then select Create key.
2

Copy the key immediately

The Your new API key dialog shows the full key once. Copy it and keep it somewhere you can find again. Senderz never shows it a second time.
The Embedded Forms settings screen shows only the first characters of a key followed by an ellipsis, so the snippet you copy from there is a template. You must paste your own full key over that placeholder before the form will work.

Install

1

Copy the snippet

Go to Settings → Embedded Forms. Pick your key in the API key dropdown, then select Copy snippet. If the dropdown says to create a key first, do that and come back.Your workspace ID is filled in for you. You can also find it under Settings → Workspace → Workspace ID.
2

Paste it into your page

Paste the whole block where you want the form to appear. The snippet looks like this:
3

Replace the key

Swap the truncated key for the full one you copied when you created it.
4

Restrict which websites can submit

Back in Settings → Embedded Forms, fill in the Allowed websites box with the sites that host your form, one per line, then select Save. See Allowed websites.
5

Test it

Submit the form on your live page with a real address. The contact appears under Contacts within a second or two.
Do not change the host in the script src. The script works out where to send submissions from its own URL, so pointing it at your own domain, or at the marketing site senderz.app, sends every submission somewhere that cannot receive it. Keep https://api.senderz.app/forms/senderz-form.js exactly as it is.
You can include the script once and put as many forms on the page as you like. Including it twice is harmless, and each form is only ever wired up once.

Form attributes

Every attribute goes on the <form> element itself.
required
Marks the form. Present with no value. Without it the script ignores the form entirely.
required
Your workspace ID.
required
Your full API key.
Replaces the confirmation text. Default: Thanks for subscribing.
Replaces the failure text. Default: We could not subscribe you. Please try again.
A label for this particular form, such as footer-signup. Stored on the contact as the embeddedFormId property so you can tell your forms apart later. Max 120 characters. It is a label you set in your own page source, so anyone can change it. Treat it as a reporting convenience, never as a restriction, which is why the allowed-websites list below is set once for the whole workspace rather than per form.
One more element matters. Put an empty <div data-senderz-msg></div> wherever you want the confirmation or error text to appear. If you leave it out, the script appends one to the end of the form, which is usually below your button.
If both data-tenant and data-key are missing, the form does not submit and shows Form is not configured. Set data-tenant and data-key. in the message element. That message is a quick way to confirm the script is loading and has found your form.

Field names decide where a value lands

This is the part worth getting right. The script reads the name attribute of every input and decides what to do with the value.
  • An input named email becomes the contact’s email address.
  • An input named phone or sms becomes the contact’s phone number.
  • Every other named input is matched against the built-in contact properties. A match is stored on the contact. A name that matches nothing is silently discarded.
Matching ignores case and surrounding spaces, and accepts the property name, its label, or a common alias.
Custom fields you created yourself under Settings → Contact Fields are not matched by an embedded form input name. Only the built-in contact properties are. If you need to collect something outside the list above, call the Forms API directly from your own backend, or capture it another way.
Two named inputs are read but never applied on this path, so do not rely on them:
  • tags is parsed and then dropped. Tag people with an automation step instead.
  • Consent flag inputs such as accepts email marketing are dropped. Consent on an embedded form is decided by the submission itself, not by a field value.
Empty inputs are skipped, so an optional field the visitor leaves blank never overwrites anything.

What a submission does

1

Match or create the contact

Senderz looks for an existing contact with the same email address or the same phone number. If it finds one it updates that contact. Otherwise it creates a new one.
2

Subscribe the channels that were supplied

An email address subscribes the email channel. A phone number in the phone or sms input subscribes the SMS channel as well.
3

Record the opt-in

The consent log records the channel, the state, the visitor’s IP address, their browser, the timestamp and the source, which reads embedded form on the contact’s activity timeline. This is the evidence record described in Consent and compliance.
4

Fill in the blanks

Recognised extra fields are written to the contact. Existing values are left alone, so a form can add a first name to a contact that had none but cannot overwrite one that was already there.
5

Fire your automations

A brand new contact fires the Created profile trigger. A contact whose consent actually changed fires Added to list.
Re-subscribing also clears the contact from your suppression list for that channel, so somebody who unsubscribed and then chose to sign up again starts receiving your marketing once more.
A repeat submission by somebody who is already subscribed changes nothing and fires neither trigger. That is deliberate. It stops a visitor who submits twice from being enrolled in your welcome automation twice.

It does not add anyone to a list

Despite the trigger being called Added to list, an embedded form does not put the contact into any Senderz list. It only fires the trigger. If you want form subscribers to end up in a specific list, build an automation on the Added to list trigger with an Update in list step that adds them. Filter that automation if the same trigger is used elsewhere in your workspace.

What is not stored

The page URL the form was submitted from is sent with every entry, but Senderz does not currently keep it. You cannot segment or report on which page somebody signed up from. Use data-form-id for that instead: it is stored, and it is per form rather than per page.

Allowed websites

Settings → Embedded Forms → Allowed websites restricts which sites may submit through your key.
  • Leave the box empty to accept submissions from any website. This is the default.
  • Add one origin per line to restrict it. Up to 20 entries.
  • A full origin such as https://example.com must match exactly, including the scheme and the port.
  • A bare host such as example.com matches either http or https.
  • Entries are lowercased and de-duplicated when you save. Anything that is not a valid web address is dropped silently on save, so re-open the box after saving to confirm what stuck.
Once the list has at least one entry, two things get rejected with a 403: a submission from a site that is not listed, and a submission that arrives with no origin at all, such as a plain curl call from a terminal.
Treat this as hygiene, not as a security boundary. Your API key is visible to anybody who views your page source. The allowed-websites list stops casual reuse of your key on another site, and it is worth setting, but it cannot be the only thing standing between you and unwanted signups. The practical protection is that the key can do nothing except subscribe somebody, and every compliance gate still applies before you actually send to them.
Submissions sent by the Senderz WordPress plugin are not checked against this list. They come from your server rather than from a browser, so there is no origin to check. See WordPress sites.

Styling

The script adds a senderz-form class to your form and a small stylesheet with sensible defaults. Override any of it from your own CSS.
Everything else, including your inputs and your button, is yours. The script does not style them. While a submission is in flight the submit button is disabled and its label is replaced with Sending…. The original label comes back when the request finishes, whether it succeeded or not. On success the form is also reset, so the fields clear.

Hook the success event

The form dispatches a senderz:subscribed event when a submission succeeds. It bubbles, so you can listen on the form or on the document.
The event detail carries email only.

Forms added after page load

The script watches the page for new content, so a form rendered later by your site’s JavaScript, a single-page app route change, or a popup library is picked up automatically. There is nothing to call and nothing to re-initialise.

Phone-only forms

The shipped script requires an email address. A visitor who fills in only the phone field sees Please enter your email address. and the form does not submit. The underlying API is happy to take a phone number with no email. If you need a phone-only signup form, post to https://api.senderz.app/api/v1/forms/subscribe from your own code rather than using the script. See the Forms API for the request shape. Phone numbers are read as Israeli by default. A local number such as 052-123-4567 is understood. For an international number, ask visitors to include the country code with a leading +.

Rate limit

Submissions are limited to 10 per minute per visitor IP address. A visitor over the limit gets a 429 and the form shows its error message. This is per visitor, not per workspace, so a busy page is unaffected. It does mean that testing the form ten times in quick succession from your own machine will lock you out for the rest of the minute.

WordPress sites

If your site runs WordPress, you do not need this snippet. The Senderz WordPress plugin can capture submissions from the form plugin you already use, with no HTML to paste and no API key in your page source. Go to Senderz Marketing → Forms in your WordPress admin. Supported form plugins are Contact Form 7, WPForms, Gravity Forms, Elementor Pro Forms, Fluent Forms, Formidable Forms and Ninja Forms. Submit each form once so its fields are detected, then map them. Two differences are worth knowing:
  • You map fields to Email, Phone, First name, Last name or a Marketing consent checkbox. That is the whole list. Anything else is not sent.
  • Consent is opt-in only, and you choose per form: never subscribe and capture the contact only, treat the whole form as a marketing opt-in, or subscribe only when the consent checkbox is ticked. Contacts are captured either way.
The plugin is installed and connected as part of the WooCommerce and WordPress setup. See WooCommerce.

Troubleshooting

The form only ever shows a generic error message, on purpose, because a visitor cannot act on a status code. To see the real reason, open your browser’s developer tools, go to the Network tab, submit the form, and inspect the subscribe request.
The script has not bound to your form. Check that the <form> element carries data-senderz-form, and that the script tag is present on the page and loading without an error in the Console tab.If the message element shows Form is not configured, the script found your form but data-tenant or data-key is missing.
The workspace ID or the API key is wrong. The most common cause is pasting the snippet straight from Settings → Embedded Forms without replacing the truncated key, which ends in an ellipsis rather than being a real key.Also check that the key has not been revoked under Settings → API Tokens.
Either the submitting site is not in your Allowed websites list, or the Embedded Forms feature has been switched off for your workspace.Check the list first. Remember that a full origin has to match exactly, so https://www.example.com does not match an entry of https://example.com. If your list is empty and you still get a 403, contact support.
You have gone over 10 submissions in a minute from the same IP address. Wait a minute. This is almost always caused by repeated testing rather than by real traffic.
The submission carried neither a usable email address nor a usable phone number, or the phone number could not be read. Check what your form actually posted in the Network tab. A phone field whose value is not a recognisable number is rejected rather than ignored.
The input name did not match a built-in contact property, so the value was discarded. Compare the input’s name attribute against the table in Field names decide where a value lands.The other cause is that the contact already had a value for that property. A form fills blanks, it does not overwrite.
Your phone input is named something other than phone or sms. A name like mobile or tel still matches the phone property, so the number is stored, but only phone and sms count as a deliberate SMS opt-in. Rename the input to phone.
Check whether the contact already existed and was already subscribed. In that case nothing changed, so no trigger fired.If the contact is new, confirm your automation is live and uses the Created profile or Added to list trigger, and that its entry filters do not exclude the contact.
Expected. An embedded form never adds anyone to a list. Build an automation on the Added to list trigger with an Update in list step. See It does not add anyone to a list.

Limits

Forms API

Call the subscribe endpoint yourself, including phone-only signups.

Consent and compliance

What the opt-in record contains and how it is used at send time.

Contacts

Contact properties, custom fields and the activity timeline.

WooCommerce and WordPress

Capture from your existing WordPress forms instead.