Skip to main content

Group Email Input API

The Group Email Input API allows you to implement fully custom subscription forms for Sendlix groups. It utilizes the same backend endpoints as the Sendlix iframe API.

Depending on your integration requirements, you can communicate directly with the HTTP API or use the official @sendlix/group helper library.


Method 1: Direct API Integration (HTTP POST)

If you do not have bot protection active for your group, you can send subscription data directly to the API endpoint using a standard multipart/form-data POST request.

Basic Fetch Example

async function insertEmailToGroup(email) {
const formData = new FormData();
formData.append("email", email);

const response = await fetch(
"https://group.sendlix.com/{your-group-id}?json=true",
{
method: "POST",
body: formData,
},
);

return await response.json();
}

Method 2: SDK Integration (@sendlix/group)

The @sendlix/group library provides programmatic helpers to subscribe users and manage client-side Proof-of-Work (PoW) bot protection.

Installation

npm install @sendlix/group

Standard Subscription (Without Bot Protection)

To subscribe an email address programmatically:

import { subscribeToGroup } from "@sendlix/group";

const result = await subscribeToGroup({
id: "your-group-id",
email: "user@example.com",
});

Adding Placeholders/Custom Fields

You can pass template placeholders (such as first and last names) along with the email:

await subscribeToGroup({
id: "your-group-id",
email: "user@example.com",
substitute: {
"##First Name##": "Alice",
"##Last Name##": "Smith",
},
});

Bot Protection & WebGPU

When Proof-of-Work (PoW) bot protection is enabled, client-side cryptographic challenges must be solved before submission.

The SDK's ProofOfWork class utilizes WebGPU for GPU-accelerated calculations when available, ensuring fast performance. If WebGPU is not supported by the client browser, it automatically falls back to the Web Crypto API using the CPU.

Manual Implementation

import { subscribeToGroup, ProofOfWork } from "@sendlix/group";

const pow = new ProofOfWork("your-group-id");

// Pre-warm the WebGPU pipeline (recommended on page load)
pow.init();

// Solve the challenge (cryptographically bound to this specific email)
const { token, nonce } = await pow.solve("user@example.com");

const result = await subscribeToGroup({
id: "your-group-id",
email: "user@example.com",
botProtection: { type: "proofOfWork", token, nonce },
});

// Release GPU and timer resources
pow.close();
important

The generated PoW token is cryptographically bound to the exact email address provided during the challenge stage. If the final submitted email does not match the email used to solve the challenge, the API will reject the subscription.


React Integration

For React applications, you can use the ProofOfWorkInput component. This component acts as a drop-in replacement for <input type="email">. It automatically runs the PoW challenge on blur (using WebGPU with CPU fallback) and injects the hidden fields pow-token and pow-nonce directly into the parent form.

Complete Form Example (with API Fetch on Submit)

Below is a complete example of a React form that captures the submission, reads the generated form data (including the hidden PoW fields), and posts it to the Sendlix API.

import { useState } from "react";
import { ProofOfWorkInput } from "@sendlix/group/react";

type PowStatus = "loading" | "processing" | "success" | "error";

export function SubscribeForm() {
const [powStatus, setPowStatus] = useState<PowStatus>();
const [formStatus, setFormStatus] = useState<
"idle" | "submitting" | "success" | "error"
>("idle");
const [errorMessage, setErrorMessage] = useState<string>("");

const groupId = "your-group-id"; // Replace with your actual group ID

const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setFormStatus("submitting");
setErrorMessage("");

// Automatically gathers email, pow-token, and pow-nonce from the inputs
const formData = new FormData(event.currentTarget);

try {
const response = await fetch(
`https://group.sendlix.com/${groupId}?json=true`,
{
method: "POST",
body: formData,
},
);

const result = await response.json();

if (result.success) {
setFormStatus("success");
} else {
setFormStatus("error");
setErrorMessage(result.message || "An error occurred.");
}
} catch (error) {
setFormStatus("error");
setErrorMessage("Network error. Please try again.");
}
};

return (
<div className="subscription-container">
{formStatus === "success" ? (
<p className="success-message">Thank you for subscribing!</p>
) : (
<form onSubmit={handleSubmit}>
<label htmlFor="email-input">Email Address</label>
<ProofOfWorkInput
id="email-input"
sendlix={{ id: groupId, onStatusChange: setPowStatus }}
placeholder="your@email.com"
required
disabled={formStatus === "submitting"}
/>

{/* Feedback for the background Proof-of-Work process */}
<div className="pow-status-indicator">
{powStatus === "loading" && (
<span>Calculating bot protection...</span>
)}
{powStatus === "processing" && <span>Refreshing token...</span>}
{powStatus === "success" && <span>Verification ready.</span>}
{powStatus === "error" && (
<span className="error">Verification failed.</span>
)}
</div>

<button
type="submit"
disabled={formStatus === "submitting" || powStatus === "loading"}
>
{formStatus === "submitting" ? "Subscribing..." : "Subscribe"}
</button>

{formStatus === "error" && (
<p className="error-message">{errorMessage}</p>
)}
</form>
)}
</div>
);
}

API Reference

subscribeToGroup(options)

subscribeToGroup(options: {
id: string;
email: string;
substitute?: Record<string, string>;
botProtection?: { type: "proofOfWork"; token: string; nonce: string };
}): Promise<GroupResponse>

subscribeToGroupWithFormData(data, id, substitute?)

subscribeToGroupWithFormData(
data: FormData,
id: string,
substitute?: Record<string, string>,
): Promise<GroupResponse>

Use this helper if you prefer to submit raw FormData programmatically.

ProofOfWork Options & Methods

const pow = new ProofOfWork(groupId: string, options?: PowOptions);

Options

OptionTypeDescription
onStatusChange(status: PowStatus) => voidTriggers on every state transition.
onRenew(result: PowResult) => voidTriggers when the token is silently renewed before expiration.

Methods

MethodReturnsDescription
pow.init()voidPre-warms the WebGPU pipeline.
pow.solve(email)Promise<PowResult>Requests a challenge and solves it. Schedules automatic renewal.
pow.close()voidCancels the renewal timer and releases GPU resources.