API Documentation

Use the Prospects API to send new prospects into CadenHQ from your app, forms, or integrations. Authenticate with an account API key; we upsert by email and start conversion campaigns for new prospects.

Overview

The CadenHQ API lets you submit prospects from outside the app (e.g. signup forms, landing pages, CRMs). Send a POST request with at least the prospect’s email. We create or update the prospect by normalized email per account.

  • New prospect – We set status to new, start the conversion campaign, and schedule follow-up actions.
  • Existing prospect – We update any provided fields and return success without re-starting the campaign.

Base URL

All endpoints are relative to the base URL for your environment (e.g. https://api.cadenhq.com). Public API routes are under /api/v1 (e.g. POST /api/v1/prospects).

Authentication

Authenticate by sending your API key in the X-CadenHQ-Key header. Keys belong to your user and can be scoped to an account (e.g. one per environment).

Header

Header X-CadenHQ-Key
Description Your API key (raw value). Keys belong to your user and can be scoped to an account; store them securely and never commit them to source control.

If the key is missing or invalid, the API responds with 401 Unauthorized and a JSON body:

{"error":"Missing or invalid X-CadenHQ-Key"}

Getting an API key: Create keys via the Rails console (e.g. ApiKey.create_for_user(User.first, account: Account.first)). The raw key is returned only once at creation; store it in your environment or secrets manager.

POST /api/v1/prospects

Create or update a prospect. The request body must include email. All other fields are optional. Prospects are upserted by (account_id, email_normalized); email is normalized (lowercased, trimmed). Valid statuses: new, converted, unsubscribed, bounced.

POST /api/v1/prospects

Submit a prospect. New prospects are enrolled in the conversion campaign; existing prospects are updated with provided attributes.

Request headers

HeaderRequiredDescription
X-CadenHQ-Key Required Account API key.
Content-Type Optional Use application/json for JSON body.

Request body parameters

ParameterTypeRequiredDescription
email string Required Prospect email. Normalized (lowercased, trimmed) for upsert.
campaign_id string (UUID) Optional Campaign ID to enroll the prospect in. If provided, the prospect is automatically added to this campaign (must be an active campaign in your account).
display_name string Optional Display name.
first_name string Optional First name.
last_name string Optional Last name.
phone string Optional Phone number.
address string Optional Street address.
city string Optional City.
state string Optional State or region.
zip string Optional Postal code.
country string Optional Country.
source string Optional Prospect source (e.g. landing_page, import).
external_id string Optional Your system’s ID for this prospect (for mapping back to your records).
utm_source string Optional UTM source.
utm_medium string Optional UTM medium.
utm_campaign string Optional UTM campaign.
utm_term string Optional UTM term.
utm_content string Optional UTM content.
metadata object Optional Extensible key-value object for custom fields. Merged with existing metadata on update.

Responses

201 Created 200 OK 400 Bad Request 401 Unauthorized 422 Unprocessable Entity

201 Created – New prospect created and campaign started. Body:

{
  "accepted": true,
  "prospect_id": "550e8400-e29b-41d4-a716-446655440000",
  "created": true
}

200 OK – Existing prospect updated. Body:

{
  "accepted": true,
  "prospect_id": "550e8400-e29b-41d4-a716-446655440000",
  "created": false
}

prospect_id is the UUID of the prospect record.

PATCH /api/v1/prospects – Update prospect and remove from campaigns

Update a prospect’s status by our prospect ID or by your external_id. When you set status to converted, unsubscribed, or bounced, the prospect is automatically removed from all campaigns (no further emails will be sent). Use this when a prospect converts, opts out, or bounces in your system.

By prospect ID (our ID)

PATCH /api/v1/prospects/:id

Update the prospect with the given CadenHQ prospect UUID.

By external_id (your ID)

PATCH /api/v1/prospects/by_external_id/:external_id

Update the prospect whose external_id matches the path (URL-encode the value if it contains special characters).

Request headers

HeaderRequiredDescription
X-CadenHQ-Key Required Account API key.
Content-Type Optional Use application/json for JSON body.

Request body parameters

ParameterTypeRequiredDescription
status string Required One of: new, converted, unsubscribed, bounced. Setting to converted, unsubscribed, or bounced removes the prospect from all campaigns.

Responses

200 OK 400 Bad Request 401 Unauthorized 404 Not Found

200 OK – Prospect updated. Body:

{
  "accepted": true,
  "prospect_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "converted"
}

When status is converted, unsubscribed, or bounced, the prospect is removed from all campaigns and will not receive further campaign emails.

Errors

Error responses are JSON objects. Typical fields:

  • error – Single string message (e.g. 400, 401).
  • errors – Array of validation messages (e.g. 422).
StatusWhen
400 Bad RequestMissing email (POST). Missing or invalid status (PATCH). Body: {"error":"..."}.
401 UnauthorizedMissing or invalid X-CadenHQ-Key. Body: {"error":"Missing or invalid X-CadenHQ-Key"}.
404 Not FoundProspect not found (PATCH by id or by external_id). Body: {"error":"Prospect not found"}.
422 Unprocessable EntityValidation failed (e.g. prospect validations). Body: {"errors": ["..."]}.

Code examples

cURL – Create or update prospect

curl -X POST https://api.cadenhq.com/api/v1/prospects \
  -H "Content-Type: application/json" \
  -H "X-CadenHQ-Key: YOUR_API_KEY" \
  -d '{
    "email": "prospect@example.com",
    "display_name": "Jane Doe",
    "campaign_id": "your-campaign-uuid",
    "source": "landing_page",
    "utm_source": "google"
  }'

JavaScript (fetch)

const response = await fetch("https://api.cadenhq.com/api/v1/prospects", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-CadenHQ-Key": process.env.CADENHQ_API_KEY,
  },
  body: JSON.stringify({
    email: "prospect@example.com",
    display_name: "Jane Doe",
    campaign_id: "your-campaign-uuid",
    source: "landing_page",
  }),
});

const data = await response.json();
if (response.ok) {
  console.log(data.created ? "Created" : "Updated", data.prospect_id);
} else {
  console.error(data.error || data.errors);
}

Ruby (Net::HTTP)

require "net/http"
require "json"

uri = URI("https://api.cadenhq.com/api/v1/prospects")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["X-CadenHQ-Key"] = ENV["CADENHQ_API_KEY"]
request.body = {
  email: "prospect@example.com",
  display_name: "Jane Doe",
  campaign_id: "your-campaign-uuid",
  source: "landing_page",
}.to_json

response = http.request(request)
data = JSON.parse(response.body)
puts response.code == "201" ? "Created" : "Updated"
puts data