Overview

The LegUpRx API exposes the same data you see in the partner portal: leads, orders, payouts, sub-accounts, medical intake links, DNA testing links, Care Service links, and Care Services bulk coupon codes. It also publishes webhook events when those records change, and a separate public catalog API for product browsing on telehealthintakeforms.com.

Base URL: /

Format: JSON in, JSON out. All authenticated responses are wrapped in an envelope - typically { data: [...] } with an optional { page: { nextCursor, limit } } for paginated endpoints.

Authentication: authenticated endpoints require a per-user API token created in Settings - API Settings. Public catalog endpoints under https://www.telehealthintakeforms.com/api are unauthenticated and use a partner_id query parameter on the intake link itself for attribution.

These APIs, webhooks, examples, and documentation are provided as is and without warranty. LegUpRx does not guarantee uninterrupted availability, exact delivery timing, or that examples cover every integration case. You are responsible for validating your implementation, handling retries and failures, and confirming that your use complies with your own business, privacy, and compliance requirements.

Authentication

API tokens are created and managed in Settings - API Settings. Each token is tied to a single portal user. Tokens can be rotated or revoked from that page at any time; revoked tokens stop working immediately.

Send the token in the Authorization header on every request:

Authorization: Bearer <token>

Tokens are scoped to your Clerk user account and start with lrx_live_. The full token is shown only once when you create or rotate it, so save it somewhere secure.

Auth-related errors

StatusCodeDescription
401missing_bearerNo Authorization: Bearer header was sent.
401invalid_tokenToken is malformed, revoked, expired, or not user-scoped.
401unknown_userToken verifies but no portal user matches its subject.
503api_keys_unavailableReserved for environments where API token verification cannot run.

Authenticated request

GET /api/v1/leads HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: application/json

List leads

GET/api/v1/leads

Returns leads collected for your partner account, ordered newest first. Paginated using a cursor; pass the nextCursor value from the previous response in the cursor query parameter to fetch the next page.

Self-hosted elective medication intakes create a lead progressively: account registration creates the row, and later intake steps can add phone and internal product/tier context to the same intake session. These rows use origin: "headless_intake" and location contains the configured product category. A row may therefore contain fewer contact fields on an early request and more when fetched again later. Product/tier context is not part of this public response.

**Elective-medication test runs are excluded by default.** Pass includeTest=true (see Query parameters) to include them. DNA, Care Services, and other lead sources are unaffected. See "Test and simulated data" for the two kinds of test run.

Query parameters

NameTypeDescription
limitinteger (1-200)Page size. Defaults to 50.
cursorstringOpaque pagination cursor from the previous response.
includeTestbooleanWhen `true`, also return leads from elective-medication test and simulated intake runs. Omit it (or pass anything else) for real leads only — that is what a CRM sync should use. Test rows always carry `isTest`, so including them never makes them look like real patients. Only elective medication intakes produce test rows; DNA, Care Services, and other lead sources are unaffected. See "Test and simulated data".

Response

Envelope: { data: LeadRow[], page: { nextCursor, limit } }. If a lead has not consented to email or SMS, the matching email or phone field is returned as No Consent.

Two fields mark a test run from an elective medication intake: - isTest: true when the lead came from a test or simulated run rather than from a person. Always present; false on every real lead and on every lead recorded before test tagging existed. - testSource: why it is a test — "provider_test_mode" or "simulation" — and null when isTest is false.

Nothing else about a test lead is altered. origin still reads headless_intake, and the name and contact details are whatever was entered, because a mangled row is a row no later reader can trust. The flag is the only marker, so filter on it rather than on a name pattern.

{
  "data": [
    {
      "id": "6f1a2c0e-4f0d-4c0a-9b41-7c9c2c0c0001",
      "partnerId": "RFMLPVN1",
      "subId": "12",
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane.doe@example.com",
      "phone": "+15555550123",
      "origin": "headless_intake",
      "location": "weight-loss",
      "questionId": null,
      "createdAt": "2026-05-09T17:42:11.214Z",
      "updatedAt": "2026-05-09T17:42:11.214Z",
      "isTest": false,
      "testSource": null
    }
  ],
  "page": {
    "nextCursor": "MjAyNi0wNS0wOVQxNzo0MjoxMS4yMTRafDZmMWEyYzBlLTRmMGQtNGMwYS05YjQxLTdjOWMyYzBjMDAwMQ==",
    "limit": 50
  }
}

Errors

StatusCodeDescription
400invalid_request`limit` is not a positive integer.
401missing_bearerAuthorization header missing.
401invalid_tokenToken is invalid, revoked, or expired.
429rate_limitedToo many requests for this token.

GET /api/v1/leads

GET /api/v1/leads?limit=50 HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: application/json

List Care Services coupons

GET/api/v1/care-services-coupons

Download Care Services bulk coupon codes owned by the authenticated partner. Each coupon is single-use, locked to a productSlug + months combination, and either redeemed by a patient via your Bulk Code Redemption URL or marked void/expired. Admin tokens may filter by any partner_id; non-admin tokens are automatically scoped to their own partner.

Use this endpoint to programmatically pull every code you have purchased (including ones still in active status that have not yet been redeemed) so you can distribute them, store them in your own system, or reconcile redemptions.

Returns JSON by default. Pass format=csv to download a spreadsheet (the same columns as the "Download all CSV" button in the partner portal Bulk Orders view) instead.

Query parameters

NameTypeDescription
statusstring (csv)Optional comma-separated subset of `active,redeemed,void,expired`.
partner_idstringAdmin only. Filter to a specific partner.
product_slugstringOptional product slug filter. Valid slugs: `urgent-care`, `urgent-plus`, `primary`, `primary-plus`, `vet-care`, `behavior`, `primary-family-plan`, `primary-family-plan-and-discount-program`. The two `primary-family-plan*` slugs are Primary Care family plans that cover a household under one membership; they map to the `primary-care` service category.
formatstringResponse format: `json` (default) or `csv`. When `csv`, the response is a `text/csv` attachment named `care-services-coupons-<YYYY-MM-DD>.csv` and the total row count is returned in the `X-Total-Count` header.
limitinteger (1-500)Page size. Defaults to 100 for JSON, 500 for CSV.
offsetinteger (>=0)Page offset. Defaults to 0. For full CSV exports of large coupon batches, paginate by incrementing `offset` and concatenating files.

Response

JSON envelope: { data: CareServiceCoupon[], total, limit, offset }. Each coupon includes code (the raw 12-character A-Z 0-9 string), productSlug, months, status, expiresAt, redeemedAt, redeemedEmail, and redeemedLyricPrimaryExternalId so you can reconcile who used what.

CSV columns (with format=csv): code, product_slug, months, status, partner_id, sub_account_id, unit_price_cents, bulk_order_id, purchaser_email, purchaser_source, created_at, expires_at, redeemed_at, redeemed_email, redeemed_lyric_primary_external_id, notes. Values are quoted; embedded quotes are escaped by doubling.

{
  "data": [
    {
      "id": "7e1cf7c0-bf56-4e90-b5be-9ec6e7d5a001",
      "code": "A1B2C3D4E5F6",
      "partnerId": "RFMLPVN1",
      "productSlug": "primary",
      "months": 1,
      "status": "active",
      "expiresAt": "2027-08-15T23:59:59Z",
      "redeemedAt": null,
      "redeemedEmail": null,
      "redeemedLyricPrimaryExternalId": null,
      "bulkOrderId": "8b27a12d-6a3f-4f33-9f6f-07ad04aa4e02",
      "purchaserEmail": "partner@example.com",
      "purchaserSource": "member_purchase",
      "unitPriceCents": 9900,
      "notes": null,
      "createdAt": "2026-05-17T18:00:00Z"
    }
  ],
  "total": 1,
  "limit": 100,
  "offset": 0
}

Errors

StatusCodeDescription
400invalid_request`product_slug`, `limit`, or `offset` is invalid.
401missing_bearerAuthorization header missing.
404not_foundCare Services API is disabled (CARE_SERVICES_API_ENABLED is off).
429rate_limitedToo many requests for this token.

GET /api/v1/care-services-coupons

# JSON (default)
GET /api/v1/care-services-coupons?status=active HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: application/json

# CSV download
GET /api/v1/care-services-coupons?format=csv&status=active,redeemed HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: text/csv

List orders

GET/api/v1/orders

Returns orders attributed to your partner account, including commission and shipping status. Sensitive fields are filtered for non-admin tokens.

**Orders from elective-medication test and simulated runs are included and flagged**, rather than withheld: an operator who ran one expects to see it. Every row carries isTest, and no row with isTest: true is ever paid — such rows are excluded from every payout and commission figure. Exclude them from your own revenue reporting the same way. DNA and Care Services orders are not produced by these test modes. See "Test and simulated data".

Response

Envelope: { data: Order[] }. The order object matches the Orders UI fields: orderId, createdDate, subAccount, product, amount, commission, status, and isTest.

isTest is true for a test or simulated run and false for every real order, including every order recorded before test tagging existed. A simulated order additionally reports status: "Simulated" and an orderId prefixed with TEST-SIM-; it is synthesized from the run's own analytics events, reports zero commission, is never payable, and has no stored order behind it at all. The orderId of a real order is never decorated, because the payment and shipping updates for that order are matched against the stored id.

{
  "data": [
    {
      "orderId": "order_102938",
      "createdDate": "2026-05-01T14:32:10.000Z",
      "subAccount": "Austin clinic",
      "product": "Injectable Semaglutide",
      "amount": 349,
      "commission": 75,
      "status": "shipped",
      "isTest": false
    }
  ]
}

Errors

StatusCodeDescription
401missing_bearerAuthorization header missing.
429rate_limitedToo many requests for this token.

GET /api/v1/orders

GET /api/v1/orders HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: application/json

Test and simulated data

Test and simulation apply **only to elective medication intakes** — the self-hosted medical intake forms returned by GET /medical-intake-links with origin: "headless_intake" on the lead. DNA testing, Care Services, managed websites, and other lead sources do not have a product test mode or a ?test=true simulation path, and they never produce isTest: true rows through those mechanisms.

How to recognise elective-med activity that came from a test rather than from a patient, and how to keep it out of your reporting.

Test activity is **tagged, not hidden**. It appears in leads, orders, and analytics with a flag on the row, and it is excluded from every money figure we calculate. Suppressing it entirely would mean an operator who ran a test could not confirm it worked; leaving it unmarked would mean a fake patient reaching your CRM and a rehearsed sale reaching your revenue report. So the row is always there and always says what it is.

There are two kinds, and they are genuinely different things:

  • **Test mode on the product.** An operator turns test mode on for a single elective medication product. The intake runs for real end to end and the treatment is created, but flagged so that it is never dispatched, prescribed, or fulfilled. The patient-facing form says so while it is on. Rows from these runs report testSource: "provider_test_mode".
  • **Simulation.** Anyone opening an elective medication intake link with ?test=true walks the whole questionnaire without any of it being submitted anywhere. It is used to check a questionnaire change or rehearse a demo. No account is created, no card is collected, and the payment step is replaced by a "Simulate payment" button that issues no payment credentials of any kind. Rows from these runs report testSource: "simulation".

The distinction matters when you are reconciling: a provider_test_mode run has a real, non-fulfilled record behind it, while a simulation run has nothing behind it anywhere. Neither is a sale.

**What this means per endpoint:**

  • GET /leads excludes test rows by default. Pass the includeTest=true query parameter (documented on that endpoint) to see them. Every row carries isTest and testSource either way.
  • GET /orders includes them, flagged with isTest. A simulated order also reads status: "Simulated" and its orderId begins TEST-SIM-.
  • Payouts and commission are never affected. No row with isTest: true is included in any payout or commission total, so a test run cannot inflate what you are owed or what you are shown as earning.

**If you sync either endpoint on a schedule, filter on isTest rather than on testSource, a name pattern, or an id prefix.** isTest is the field that answers "should this be counted"; testSource only explains why not, and an id prefix exists on simulated orders alone.

Response

Both fields are on the lead row itself, so no extra request is needed to tell test activity apart:

  • isTest (boolean, always present): true for a test or simulated elective-medication run. false for real activity, and false on every row recorded before test tagging existed, which is correct — those paths only ever produced real rows.
  • testSource (string or null): "provider_test_mode" or "simulation", and null whenever isTest is false.

On orders, isTest is present and carries the same meaning; the reason is not exposed as a separate field there.

{
  "data": [
    {
      "id": "6f1a2c0e-4f0d-4c0a-9b41-7c9c2c0c0002",
      "partnerId": "RFMLPVN1",
      "subId": "12",
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane.doe@example.com",
      "phone": "+15555550123",
      "origin": "headless_intake",
      "location": "weight-loss",
      "questionId": null,
      "createdAt": "2026-05-09T17:42:11.214Z",
      "updatedAt": "2026-05-09T17:42:11.214Z",
      "isTest": true,
      "testSource": "simulation"
    }
  ],
  "page": {
    "nextCursor": null,
    "limit": 50
  }
}

Test and simulated data

# Real leads only (the default)
GET /api/v1/leads HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: application/json

# Include test and simulated runs
GET /api/v1/leads?includeTest=true HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: application/json

List payouts

GET/api/v1/payouts

Returns payout records for the authenticated partner.

Response

Envelope: { data: Payout[] }. Admin-only receipt and note fields are not included.

{
  "data": [
    {
      "recordId": "5e9036aa-7772-40b6-9efa-38b5d782fb73",
      "email": "partner@example.com",
      "createdAt": "2026-05-01",
      "payoutDate": "2026-05-01",
      "payoutAmount": 1234.56,
      "status": "paid"
    }
  ]
}

Errors

StatusCodeDescription
401missing_bearerAuthorization header missing.

GET /api/v1/payouts

GET /api/v1/payouts HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Accept: application/json

Sub-accounts

GET/api/v1/sub-accounts

List, create, and delete sub-accounts. Sub-accounts let you attribute orders, leads, and links to a specific staffer or affiliate using the subId field.

Response

GET envelope: { data: PortalSubAccount[] }. POST returns 201 with { data: PortalSubAccount }. DELETE returns { data: { ok: true } }.

{
  "data": [
    {
      "id": "52c4a0eb-d109-4b44-90df-f0f0864c0195",
      "email": "partner@example.com",
      "subId": "12",
      "nickname": "Austin clinic",
      "createdAt": "2026-04-01T10:00:00.000Z",
      "updatedAt": "2026-04-01T10:00:00.000Z"
    }
  ]
}

Errors

StatusCodeDescription
400invalid_requestMissing or invalid `subId`/`nickname`/`recordId`.
401missing_bearerAuthorization header missing.
404not_foundSub account not found (DELETE).
409invalid_requestA sub-account with that `subId` already exists.

GET /api/v1/sub-accounts

GET /api/v1/sub-accounts HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN

POST /api/v1/sub-accounts HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Content-Type: application/json

{"subId":"12","nickname":"Austin clinic"}

DELETE /api/v1/sub-accounts HTTP/1.1
Host: app.leguprx.com
Authorization: Bearer $LEGUPRX_API_TOKEN
Content-Type: application/json

{"recordId":"52c4a0eb-d109-4b44-90df-f0f0864c0195"}

Webhooks

Webhooks let you react in real time to leads, orders, and payouts. Configure endpoints in Settings - API Settings - Webhooks. Each account can have up to 10 endpoints, and each endpoint receives the event types you select.

  • HTTPS only. Plain http:// URLs are rejected at save time.
  • Deliveries retry up to 6 times with exponential backoff (a few seconds, then minutes, then hours) until your endpoint returns a 2xx status.
  • Each delivery is signed with HMAC-SHA256. Verifying the signature is optional, but strongly recommended - see Signature verification.
  • Webhook secrets are optional. If you do not set one, deliveries still work, but you should treat signature verification as disabled for that endpoint.
  • lead.created deliveries can optionally be delayed by 1 minute, 5 minutes, or 1 hour to give the lead time to enter their phone number on the next intake step before you receive the webhook.
  • You can attach a single optional custom header (for example a shared bearer token) per endpoint, which we send on every delivery.

Signature verification

Signature verification is optional, but strongly recommended. Every delivery includes an X-LegUpRx-Signature header in the form:

X-LegUpRx-Signature: t=<unix_timestamp>,v1=<sha256_hex>

The signed string is t + '.' + body, where body is the exact raw HTTP request body (do not re-serialize the JSON). Compute HMAC-SHA256 with your endpoint secret as the key and compare in constant time. Reject deliveries whose t is more than 5 minutes old to prevent replays.

We also send X-LegUpRx-Event (the event name) and X-LegUpRx-Delivery (a unique delivery id, useful for idempotent processing).

Verify webhook signature

POST /your-webhook-endpoint HTTP/1.1
Host: yourdomain.com
Content-Type: application/json
X-LegUpRx-Signature: t=1715300000,v1=4f0b8f3bb12c2f70d4e3fb28c85dd438df9ad1186a2c9e5d13afd7b0ac54e8c9
X-LegUpRx-Event: lead.created
X-LegUpRx-Delivery: 5c570818-e918-489e-bebb-fbd9a5f6a47d

{"id":"5c570818-e918-489e-bebb-fbd9a5f6a47d","event":"lead.created","created_at":"2026-05-09T17:42:11.214Z","data":{"lead":{"id":"6f1a2c0e-4f0d-4c0a-9b41-7c9c2c0c0001"}}}

lead.created

EVENTlead.created

Fires when a new lead is captured for your partner account through a webhook-enabled lead source, including managed websites, public links, and sub-account collection forms. Self-hosted medical-intake leads are available through `GET /leads` with `origin: "headless_intake"`, but their progressive internal upsert does not currently dispatch this webhook.

Optional 1 minute, 5 minute, or 1 hour delivery delay can be configured per endpoint. The delay gives the lead time to enter their phone number on the next intake step before you receive the webhook, so the payload is more useful for follow-up.

Example payload

{
  "id": "0d8a0fe2-b19c-4f61-bf65-7a16c0371176",
  "event": "lead.created",
  "created_at": "2026-05-09T17:42:11.214Z",
  "data": {
    "lead": {
      "id": "6f1a2c0e-4f0d-4c0a-9b41-7c9c2c0c0001",
      "partnerId": "RFMLPVN1",
      "subId": "12",
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane.doe@example.com",
      "phone": "+15555550123",
      "origin": "headless_intake",
      "location": "weight-loss",
      "questionId": null,
      "createdAt": "2026-05-09T17:42:11.214Z",
      "updatedAt": "2026-05-09T17:42:11.214Z",
      "isTest": false,
      "testSource": null
    }
  }
}

lead.enriched

EVENTlead.enriched

Fires when a previously captured lead on a webhook-enabled source has its phone number filled in or updated after the initial capture. Self-hosted medical-intake rows can be progressively enriched and observed through `GET /leads`, but that internal enrichment does not currently dispatch this webhook.

Example payload

{
  "id": "ff34b08c-58ef-4edc-81c9-b12f94e57a11",
  "event": "lead.enriched",
  "created_at": "2026-05-09T18:11:02.005Z",
  "data": {
    "lead": {
      "id": "6f1a2c0e-4f0d-4c0a-9b41-7c9c2c0c0001",
      "partnerId": "RFMLPVN1",
      "subId": "12",
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane.doe@example.com",
      "phone": "+15555550123",
      "origin": "headless_intake",
      "location": "weight-loss",
      "questionId": null,
      "createdAt": "2026-05-09T17:42:11.214Z",
      "updatedAt": "2026-05-09T17:42:11.214Z",
      "isTest": false,
      "testSource": null
    },
    "previousPhone": "",
    "currentPhone": "+15555550123"
  }
}

order.created

EVENTorder.created

Fires when a new order is attributed to your partner account, including its commission breakdown and shipping status.

Example payload

{
  "id": "736b6ed2-1e5e-4c13-9faa-31a3d42f0909",
  "event": "order.created",
  "created_at": "2026-05-09T19:01:00.000Z",
  "data": {
    "source": "admin",
    "order": {
      "orderId": "order_102938",
      "createdDate": "2026-05-01T14:32:10.000Z",
      "subAccount": "Austin clinic",
      "product": "Injectable Semaglutide",
      "amount": 349,
      "commission": 75,
      "status": "shipped",
      "isTest": false
    }
  }
}

payout.created

EVENTpayout.created

Fires when a payout record is created for your account.

Example payload

{
  "id": "b52f2e65-34c8-4fae-874e-e9f6de2e592d",
  "event": "payout.created",
  "created_at": "2026-05-09T20:00:00.000Z",
  "data": {
    "payout": {
      "recordId": "5e9036aa-7772-40b6-9efa-38b5d782fb73",
      "email": "partner@example.com",
      "createdAt": "2026-05-01",
      "payoutDate": "2026-05-01",
      "payoutAmount": 1234.56,
      "status": "paid"
    }
  }
}

Weight loss

GEThttps://www.telehealthintakeforms.com/api/weight-loss

Active weight-loss products available for distribution.

These endpoints are unauthenticated. To attribute traffic to your account, distribute intake links that already include your partner_id, for example:

https://www.telehealthintakeforms.com/api/weight-loss?partner_id=RFMLPVN1

Typical response headers include Content-Type: application/json; Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate with Cdn-Cache-Control: no-store; Access-Control-Allow-Methods: GET, OPTIONS; Access-Control-Allow-Headers: Content-Type.

Example response

{
  "products": [
    {
      "id": "06c641ae-bebd-4e53-b267-f0e6b5b4e272",
      "name": "Injectable Lipo-B",
      "image": "https://www.telehealthintakeforms.com/api/files/a77ee64e-4a59-4d4d-81be-f042bae0b9b3",
      "imageKind": "file",
      "imageFileId": "a77ee64e-4a59-4d4d-81be-f042bae0b9b3",
      "imageSourceUrl": "https://www.telehealthintakeforms.com/api/files/a77ee64e-4a59-4d4d-81be-f042bae0b9b3",
      "intakeUrl": "https://medical.leguprecovery.com/start-online-visit/lipob-intake-vialsrx?partner_id=RFMLPVN1",
      "price1Month": "$574.00",
      "price3Month": "$449.00",
      "price6Month": "$849.00",
      "details": "Available Nationwide"
    }
  ],
  "count": 11
}
Example response for https://www.telehealthintakeforms.com/api/weight-loss?partner_id=RFMLPVN1. Counts and product lists can change as catalog availability changes.

GET /weight-loss

GET /api/weight-loss?partner_id=RFMLPVN1 HTTP/1.1
Host: www.telehealthintakeforms.com
Accept: application/json

Men's health

GEThttps://www.telehealthintakeforms.com/api/mens-health

Active men's health products.

These endpoints are unauthenticated. To attribute traffic to your account, distribute intake links that already include your partner_id, for example:

https://www.telehealthintakeforms.com/api/mens-health?partner_id=RFMLPVN1

Typical response headers include Content-Type: application/json; Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate with Cdn-Cache-Control: no-store; Access-Control-Allow-Methods: GET, OPTIONS; Access-Control-Allow-Headers: Content-Type.

Example response

{
  "products": [
    {
      "id": "c6016e64-2ef3-4dad-b999-be0efe6289fb",
      "name": "Cialis",
      "image": "https://www.telehealthintakeforms.com/api/files/c23ad164-fc36-4a39-b26d-065ab2df21fb",
      "imageKind": "file",
      "imageFileId": "c23ad164-fc36-4a39-b26d-065ab2df21fb",
      "imageSourceUrl": "https://www.telehealthintakeforms.com/api/files/c23ad164-fc36-4a39-b26d-065ab2df21fb",
      "intakeUrl": "https://medical.leguprecovery.com/start-online-visit/ed?partner_id=RFMLPVN1",
      "price1Month": "$549.00",
      "price3Month": "$0.00",
      "price6Month": "$0.00",
      "details": "Not available in South Carolina"
    }
  ],
  "count": 4
}
Example response for https://www.telehealthintakeforms.com/api/mens-health?partner_id=RFMLPVN1. Counts and product lists can change as catalog availability changes.

GET /mens-health

GET /api/mens-health?partner_id=RFMLPVN1 HTTP/1.1
Host: www.telehealthintakeforms.com
Accept: application/json

Women's health

GEThttps://www.telehealthintakeforms.com/api/womens-health

Active women's health products.

These endpoints are unauthenticated. To attribute traffic to your account, distribute intake links that already include your partner_id, for example:

https://www.telehealthintakeforms.com/api/womens-health?partner_id=RFMLPVN1

Typical response headers include Content-Type: application/json; Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate with Cdn-Cache-Control: no-store; Access-Control-Allow-Methods: GET, OPTIONS; Access-Control-Allow-Headers: Content-Type.

Example response

{
  "products": [
    {
      "id": "694759f6-2b17-4128-ba8c-2dfa274fb87e",
      "name": "Enclomiphene",
      "image": "https://www.telehealthintakeforms.com/api/files/7412c668-3b12-4595-b108-2a96a809f31f",
      "imageKind": "file",
      "imageFileId": "7412c668-3b12-4595-b108-2a96a809f31f",
      "imageSourceUrl": "https://www.telehealthintakeforms.com/api/files/7412c668-3b12-4595-b108-2a96a809f31f",
      "intakeUrl": "https://medical.leguprecovery.com/start-online-visit/enclomiphene-intake?partner_id=RFMLPVN1",
      "price1Month": "$224.00",
      "price3Month": "$0.00",
      "price6Month": "$0.00",
      "details": "COMING SOON 5/31/2026! Available Nationwide!"
    }
  ],
  "count": 4
}
Example response for https://www.telehealthintakeforms.com/api/womens-health?partner_id=RFMLPVN1. Counts and product lists can change as catalog availability changes.

GET /womens-health

GET /api/womens-health?partner_id=RFMLPVN1 HTTP/1.1
Host: www.telehealthintakeforms.com
Accept: application/json

Hair loss

GEThttps://www.telehealthintakeforms.com/api/hair-loss

Active hair-loss products.

These endpoints are unauthenticated. To attribute traffic to your account, distribute intake links that already include your partner_id, for example:

https://www.telehealthintakeforms.com/api/hair-loss?partner_id=RFMLPVN1

Typical response headers include Content-Type: application/json; Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate with Cdn-Cache-Control: no-store; Access-Control-Allow-Methods: GET, OPTIONS; Access-Control-Allow-Headers: Content-Type.

Example response

{
  "products": [
    {
      "id": "b2abf87a-b350-4864-9df5-08cc44c4bf28",
      "name": "Finasteride Tablets",
      "image": "https://www.telehealthintakeforms.com/api/files/073ba9d7-a446-4471-be65-41a3ca2a451e",
      "imageKind": "file",
      "imageFileId": "073ba9d7-a446-4471-be65-41a3ca2a451e",
      "imageSourceUrl": "https://www.telehealthintakeforms.com/api/files/073ba9d7-a446-4471-be65-41a3ca2a451e",
      "intakeUrl": "https://medical.leguprecovery.com/start-online-visit/hairloss?partner_id=RFMLPVN1",
      "price1Month": "$124.00",
      "price3Month": "$0.00",
      "price6Month": "$0.00",
      "details": "Available Nationwide"
    }
  ],
  "count": 3
}
Example response for https://www.telehealthintakeforms.com/api/hair-loss?partner_id=RFMLPVN1. Counts and product lists can change as catalog availability changes.

GET /hair-loss

GET /api/hair-loss?partner_id=RFMLPVN1 HTTP/1.1
Host: www.telehealthintakeforms.com
Accept: application/json

Wellness

GEThttps://www.telehealthintakeforms.com/api/wellness

Active wellness and longevity products.

These endpoints are unauthenticated. To attribute traffic to your account, distribute intake links that already include your partner_id, for example:

https://www.telehealthintakeforms.com/api/wellness?partner_id=RFMLPVN1

Typical response headers include Content-Type: application/json; Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate with Cdn-Cache-Control: no-store; Access-Control-Allow-Methods: GET, OPTIONS; Access-Control-Allow-Headers: Content-Type.

Example response

{
  "products": [
    {
      "id": "a9bef966-e60f-4875-893e-b18c9e6a2ae4",
      "name": "Injectable B-12",
      "image": "https://www.telehealthintakeforms.com/api/files/f420cbc3-ff2e-4644-92b8-a2f04f845c60",
      "imageKind": "file",
      "imageFileId": "f420cbc3-ff2e-4644-92b8-a2f04f845c60",
      "imageSourceUrl": "https://www.telehealthintakeforms.com/api/files/f420cbc3-ff2e-4644-92b8-a2f04f845c60",
      "intakeUrl": "https://medical.leguprecovery.com/start-online-visit/b12-intake?partner_id=RFMLPVN1",
      "price1Month": "$249.00",
      "price3Month": "$0.00",
      "price6Month": "$0.00",
      "details": "Not available in Louisiana, Mississippi, New Mexico"
    }
  ],
  "count": 6
}
Example response for https://www.telehealthintakeforms.com/api/wellness?partner_id=RFMLPVN1. Counts and product lists can change as catalog availability changes.

GET /wellness

GET /api/wellness?partner_id=RFMLPVN1 HTTP/1.1
Host: www.telehealthintakeforms.com
Accept: application/json

All products

GEThttps://www.telehealthintakeforms.com/api/all-products

Combined active medication link product catalog across all medication categories.

These endpoints are unauthenticated. To attribute traffic to your account, distribute intake links that already include your partner_id, for example:

https://www.telehealthintakeforms.com/api/all-products?partner_id=RFMLPVN1

Typical response headers include Content-Type: application/json; Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate with Cdn-Cache-Control: no-store; Access-Control-Allow-Methods: GET, OPTIONS; Access-Control-Allow-Headers: Content-Type.

Example response

{
  "products": [
    {
      "id": "c6016e64-2ef3-4dad-b999-be0efe6289fb",
      "name": "Cialis",
      "image": "https://www.telehealthintakeforms.com/api/files/c23ad164-fc36-4a39-b26d-065ab2df21fb",
      "imageKind": "file",
      "imageFileId": "c23ad164-fc36-4a39-b26d-065ab2df21fb",
      "imageSourceUrl": "https://www.telehealthintakeforms.com/api/files/c23ad164-fc36-4a39-b26d-065ab2df21fb",
      "intakeUrl": "https://medical.leguprecovery.com/start-online-visit/ed?partner_id=RFMLPVN1",
      "price1Month": "$549.00",
      "price3Month": "$0.00",
      "price6Month": "$0.00",
      "details": "Not available in South Carolina"
    }
  ],
  "count": 28
}
Example response for https://www.telehealthintakeforms.com/api/all-products?partner_id=RFMLPVN1. Counts and product lists can change as catalog availability changes.

GET /all-products

GET /api/all-products?partner_id=RFMLPVN1 HTTP/1.1
Host: www.telehealthintakeforms.com
Accept: application/json

Embed Care Services

Partners can embed their Care Services catalog on their own site with the iframe URL shown in their Care Services section. Treat that URL like a shareable tracking link, not a secret. The embedded view only renders services the partner can currently access and respects category feature flags.

Replace the placeholder src below with the Care Services embed URL copied from the partner portal. Do not build this from the API docs domain. The payment-link domain accepts a few query parameters:

  • partner_id (required): your partner attribution code; auto-populated by the partner portal's "Copy URL" buttons.
  • service: one of primary-care, urgent-care, mental-health, dermatology, veterinary-care. Filters the embedded catalog.
  • m: term in months: 1, 3, 6, or 12. Pre-selects a duration on the intake.
  • sub: optional sub-account number (1-250) to attribute the order to a downstream account.
  • redeem=true: switches the page from the Stripe checkout flow to the Bulk Code Redemption screen. Patients enter a 12-character coupon code (no card collected) and we provision the care services membership directly. Use the "Copy Bulk Code Redemption URL" button on each Care Services row to generate this URL.
<iframe
  id="leguprx-care-services"
  src="PASTE_YOUR_CARE_SERVICES_EMBED_URL_HERE"
  style="width:100%;border:0;min-height:700px;border-radius:16px;"
  loading="lazy"
></iframe>
<script>
  window.addEventListener('message', (event) => {
    if (event.data?.type !== 'leguprx-api-docs-height') return;
    document.getElementById('leguprx-care-services').style.height = event.data.height + 'px';
  });
</script>
{
  "placeholderUrl": "PASTE_YOUR_CARE_SERVICES_EMBED_URL_HERE",
  "whereToFindIt": "Partner Links > Care Services in the partner portal",
  "notes": "Use the URL shown in the partner Care Services section. It already includes the correct partner attribution."
}

Errors and rate limits

All errors use a consistent envelope:

{
  "error": {
    "code": "invalid_request",
    "message": "limit must be a positive integer."
  }
}

Rate limits are enforced per API token, per endpoint group. When a request is rate-limited, the response includes X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers when applicable.

StatusCodeDescription
401missing_bearerRequest did not include `Authorization: Bearer <token>`.
401invalid_tokenToken is malformed, revoked, expired, or not user-scoped.
401unknown_userThe token verifies, but no portal user matches its subject.
403forbiddenThe account is inactive or the endpoint is disabled for this account (for example DNA links).
404not_foundThe referenced record does not exist or does not belong to this account.
400invalid_requestA query/body parameter failed validation.
429rate_limitedPer-token rate limit exceeded for this endpoint.
500internal_errorUnexpected server error. Safe to retry with backoff.
503api_keys_unavailableReserved for environments where API token verification cannot run.