Code Examples

Complete, production-ready examples for submitting leads from cURL, JavaScript, Python, PHP, and Go.

Basic Submission

A full submission example with traffic data and Q&A answers:

curl -X POST https://api.hermon.io/api/opt-in/submit \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_your_key_here" \
  -d '{
    "email": "john@example.com",
    "first_name": "John",
    "last_name": "Smith",
    "source": "LANDING_PAGE",
    "phone_e164": "+14155552671",
    "phone_country": "US",
    "provider_form_name": "Free Strategy Session",
    "external_reference": "sub_abc123",
    "traffic": {
      "utm_source": "facebook",
      "utm_medium": "paid",
      "utm_campaign": "q1-coaching",
      "landing_page": "https://yoursite.com/apply"
    },
    "questions_answers": [
      {
        "question": "What is your biggest challenge?",
        "answer": "Scaling my business",
        "position": 0
      }
    ]
  }'
Replace sk_live_your_key_here with your actual API key, stored in an environment variable — never hardcoded in source files.

With Idempotency

Use external_reference to prevent duplicate leads when your webhook or form might fire multiple times:

const response = await fetch("https://api.hermon.io/api/opt-in/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.HERMON_API_KEY,
  },
  body: JSON.stringify({
    email: "john@example.com",
    first_name: "John",
    source: "TYPEFORM",
    // Use your form's submission ID as the external reference
    external_reference: "typeform-abc123xyz",
  }),
});

Setter Attribution

Automatically attribute a setter by passing their Clerk user ID in traffic.utm_content:

// Setter attribution via utm_content
// If utm_content starts with "user_" (a Clerk user ID),
// Hermon assigns that user as the setter on the lead.

const response = await fetch("https://api.hermon.io/api/opt-in/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.HERMON_API_KEY,
  },
  body: JSON.stringify({
    email: "john@example.com",
    first_name: "John",
    source: "LANDING_PAGE",
    traffic: {
      utm_source: "facebook",
      utm_medium: "paid",
      utm_campaign: "q1-coaching",
      // This assigns user_setterabc123 as the setter
      utm_content: "user_setterabc123",
    },
  }),
});

Handling Responses

Always check the response status and handle errors gracefully:

const response = await fetch("https://api.hermon.io/api/opt-in/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.HERMON_API_KEY,
  },
  body: JSON.stringify(payload),
});

if (response.status === 429) {
  // Rate limited — back off and retry
  throw new Error("Rate limit exceeded");
}

if (!response.ok) {
  const error = await response.json();
  console.error("Submission failed:", error.errors);
  throw new Error(error.message);
}

const data = await response.json();
const { lead_id, is_new_lead } = data.data;

console.log(is_new_lead ? "New lead created:" : "Existing lead updated:", lead_id);