The OpenAPI spec, the integration brief and the project vocabulary are served by the platform and change without a deploy on your side. Fetch them when you build; do not paste them into your repository or your prompt. Every prompt on this page points at a URL for that reason.
Build against hellojade with an AI coding agent.
This page is written for the agent as much as for you. It names every public hellojade API, where the live machine-readable contract for each one is, how authentication and rate limits work, and what counts as done — with a prompt per service you can paste as-is.
Three rules that decide whether the integration is right.
Every one of them is an answer to a mistake a coding agent makes by default.
A test lead with a plausible name and phone number reaches a real salesperson's phone and a real customer's CRM. The sanctioned probe is a real key with an empty body: the endpoint authenticates first and validates second, so {} answers 422 — proof the key works — and nothing is stored. For a live round trip, ask for a sandbox key.
The integration brief ends with a definition of done (§8): a list of checks that can each be run and seen. An agent that reports "it looks right" is not done; one that reports the status code of each check is. The list is reproduced below.
What is public, measured on 2026-09-03.
Only surfaces that answered from the public internet on that day are listed as public. Anything else is named so an agent does not go looking for it.
| Surface | Endpoint | Auth | Status | Machine-readable source |
|---|---|---|---|---|
| Partner Intake API | POSThttps://intake.hellojade.ai/v1/intake | X-API-Key, issued per lead source | public, key-gated | openapi.json · INTEGRATION.md |
| Project vocabulary | GEThttps://intake.hellojade.ai/v1/vocabulary | none | public | the response itself (JSON, cached 300 s) |
| Intake health | GEThttps://intake.hellojade.ai/healthz | none | public | JSON: ok, pending, dead, store_writable |
| Subscription plan feed | GEThttps://subscription.hellojade.ai/api/plans | none | public | the response itself (JSON array of plans) |
| Subscription catalog | GEThttps://subscription.hellojade.ai/api/catalog | none | not public | in the service's source; answered 404 from production on 2026-09-03 — treat as not yet public |
| Subscription checkout | POSThttps://subscription.hellojade.ai/api/checkout/… | browser origin allowlist | not public | for the hellojade.ai storefront only (Stripe); CORS-locked, not a third-party API |
| Tenant forms endpoint | POSThttps://forms.hellojade.ai/v1/forms/{tenant}/{form_key} | X-API-Key, issued to hellojade customers | public, key-gated | no published spec; the contract is on this page |
| Forms health | GEThttps://forms.hellojade.ai/healthz | none | public | plain text ok |
| Forms ops (leads, report, metrics) | GEThttps://forms.hellojade.ai/v1/leads … | — | not public | VPC-only; answers 404 through the public edge by design |
| This site's graph | GEThttps://hellojade.ai/assets/site.data | none | public | /llms.txt carries the table schema; every page has JSON-LD |
| Actions tier (CRM, email) | — | — | not public | private; reachable only from the intake service over mutual TLS |
Provide leads to a hellojade customer.
One outbound HTTPS call per lead. No SDK required, no OAuth, no webhook to host, no polling.
| Auth | X-API-Key header. The key is issued by hellojade per lead source; its registered label is the lead's source — the body cannot set it. |
|---|---|
| Idempotency | Idempotency-Key header, namespaced to you and stable across retries. Dedupe is scoped to the customer, not to your key, so a bare 1234 collides silently with another source's 1234. 202 is new; 200 is a duplicate carrying the original event_id. |
| Rate limits | 600 req/min per key (burst 101). 120 req/min per source IP (burst 31), applied before authentication. Retry-After: 1 on both — a floor, not a strategy. |
| Body cap · timeout | 64 KiB, enforced before auth. Use a 20 s client timeout; the handler is bounded at 20 s and answers in milliseconds. |
| Required fields | first_name, last_name, phone. Everything else is optional; do not invent placeholders. |
| What 202 means | Committed to disk with its delivery record, in one transaction. Mark the lead sent at your end; there is nothing to poll. |
| Sanctioned probe | A real key with an empty body: {} → 422. Authenticates, resolves the tenant, stores nothing, consumes no idempotency key. |
The key check, in four languages
curl -i -X POST https://intake.hellojade.ai/v1/intake \
-H "X-API-Key: $HELLOJADE_API_KEY" \
-H 'Content-Type: application/json' \
-d '{}'
# 422 → key valid, nothing stored · 401 → key wrong · 429 → per-IP limit, wait 1sreq, _ := http.NewRequest("POST", "https://intake.hellojade.ai/v1/intake", strings.NewReader("{}"))
req.Header.Set("X-API-Key", os.Getenv("HELLOJADE_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := (&http.Client{Timeout: 20 * time.Second}).Do(req)
if err != nil { log.Fatal(err) }
defer res.Body.Close()
// 422 = key valid and nothing stored; 401 = key wrong; 429 = wait Retry-After
fmt.Println(res.StatusCode)const res = await fetch('https://intake.hellojade.ai/v1/intake', {
method: 'POST',
headers: { 'X-API-Key': process.env.HELLOJADE_API_KEY, 'Content-Type': 'application/json' },
body: '{}',
signal: AbortSignal.timeout(20_000),
});
// 422 = key valid and nothing stored; 401 = key wrong; 429 = wait Retry-After
console.log(res.status, await res.json());import os, urllib.request, json
req = urllib.request.Request(
"https://intake.hellojade.ai/v1/intake", data=b"{}", method="POST",
headers={"X-API-Key": os.environ["HELLOJADE_API_KEY"], "Content-Type": "application/json"})
try:
urllib.request.urlopen(req, timeout=20)
except urllib.error.HTTPError as e:
# 422 = key valid and nothing stored; 401 = key wrong; 429 = wait Retry-After
print(e.code, json.load(e))The prompt
You are integrating my lead source with the hellojade Partner Intake API.
Read these first, in this order, and treat them as the source of truth over anything you already know:
1. https://intake.hellojade.ai/api/INTEGRATION.md (the integration brief — read it front to back)
2. https://intake.hellojade.ai/api/openapi.json (the OpenAPI 3.0 contract — generate types from it, do not hand-write them)
3. https://intake.hellojade.ai/v1/vocabulary (the live project_area / project_service vocabulary — fetch it, never hard-code it)
Rules that override your defaults:
- The base URL is https://intake.hellojade.ai and is configuration, not a constant. There is no http:// listener.
- Auth is the X-API-Key header. The key comes from the environment (HELLOJADE_API_KEY). It must never appear in source, a URL, a log line or an error message.
- Always send Idempotency-Key = "<our-namespace>:<our stable lead id>". Never a timestamp, never regenerated per attempt. 202 = new, 200 = duplicate with the same event_id; both are success.
- Retry only on 5xx, timeouts, and 429 (sleep at least Retry-After, then grow the wait with jitter; a 429 does not consume a delivery attempt). Never retry any other 4xx. A 422 means the body needs fixing.
- Do not send a "source" field; the key's registered label is the source. Do not send "extra". Send unmodeled fields at the top level; they are preserved.
- Rate limits: 600 requests/min per key (burst 101); 120/min per source IP (burst 31), applied before authentication.
- NEVER send a test lead with a real-looking name or phone. The only sanctioned test against production is a real key with an empty JSON body: POST {} must return 422 validation_failed, which proves the key works and stores nothing. For a live round trip, stop and ask me for a sandbox key.
Definition of done — run each check and report the observed status code, not an opinion:
1. Key check returns 422 — The §1 key check returns 422, not 401, against the key you will ship with.
2. A minimal lead returns 202 — A lead with first_name, last_name and phone returns 202.
3. A repeat returns 200 with the same event_id — The same lead sent twice with the same Idempotency-Key returns 202 then 200, with the same event_id, and your system records one send, not two.
4. The Idempotency-Key is namespaced and stable — It is prefixed with something only you use and does not change across retries — not a bare integer, not a timestamp, not regenerated per attempt.
5. A 422 surfaces every failing field — A lead missing all three required fields returns 422 and your code reports all three names, not just the first.
6. A 503 retries with backoff and loses nothing — Point at a dead host: the lead is retried with growing, jittered backoff and is never dropped.
7. A 422 does not retry — Retrying an unchanged 422 burns your rate limit and fixes nothing.
8. A 429 waits and does not spend an attempt — Sleep for Retry-After (a floor of one second), grow the wait on repeats, and do not count it as a delivery attempt.
9. The key appears in no log, error, trace or commit — Grep for it before you call the integration done.
10. An unmappable project_area is sent raw — Unrecognized values are stored and flagged, never rejected — send what your system calls it.
11. event_id is stored and request_id is logged — event_id against your lead record; request_id on every failure — that is what makes a support question answerable.
12. The base URL is https:// and is configuration — Nothing listens on port 80, and the host must not be a constant in the code.
Store event_id against our lead record and log request_id on every failure.Read the published plans.
A public, read-only JSON feed of the subscription plans hellojade has published. Useful for a partner portal or a comparison page; not a purchase API.
| Auth | None. |
|---|---|
| Shape | A JSON array. Each plan: slug, name, customer, amount_cents, interval, items[{title, detail}]. Read it; do not assume more. |
| Rate limits | None published. Cache for at least five minutes; fetch once per build where you can. |
| Writes | None for third parties. /api/checkout/* serves the hellojade.ai storefront and is CORS-locked to it; /api/catalog is in the service's source but not reachable from production today. |
curl -s https://subscription.hellojade.ai/api/plans | head -c 400
# 200 application/json — a JSON array; each element has slug, name, amount_cents, interval, items[]The prompt
Read the public hellojade subscription plan feed and use it as the only source of plan data.
Source (fetch it live; do not paste it into the code): https://subscription.hellojade.ai/api/plans
What it is: a GET with no authentication returning a JSON array of published plans. Each element carries at least slug, name, customer, amount_cents, interval and an items array of {title, detail}. Treat unknown fields as data to preserve, not errors.
Rules:
- Read-only. There is no write API here for third parties; the checkout endpoints on the same host are for the hellojade.ai storefront only and are CORS-locked to it. Do not call them.
- GET /api/catalog is not public today (it answers 404 from production as of 2026-09-03); do not build on it.
- No rate limit is published for the feed. Fetch it once per build or cache it for at least five minutes at run time; do not poll it.
- Prices are amount_cents in USD per interval. Never format a price from a field you have not read.
Done when: a GET returns 200 with a non-empty array, your code parses every element without a hand-written field list, and it fails closed (no plan shown) when the feed is unreachable rather than showing a stale or invented price.Post a customer's website forms.
The canonical forms receiver for hellojade customers. If you build or host a site for one of them, this is where its forms go — with the key that customer was issued.
| Auth | X-API-Key, issued to the hellojade customer (the tenant). The path's {tenant} must match the key's tenant or the answer is 403. |
|---|---|
| Who gets a key | hellojade customers and the people building their sites. It is not a partner API; a lead provider uses the Partner Intake API above. |
| Rate limits | Per source IP and per tenant, both answering 429. The values are not published — retry with backoff, never in a hot loop. |
| Spam handling | A non-empty hidden website field is a bot signal and delivers nothing. Content is also scored server-side; a stored-but-undelivered submission is not an error you can see from the client. |
| Sanctioned probe | A real key and a body that fails required-field validation: {"form_key":"contact","email":"x@example.invalid"} → 400 missing_required. That proves the key and form key resolve while storing and emailing nothing. |
| Not public | /v1/leads, /v1/report, /metrics, /debug/vars — VPC-only, 404 through the edge by design. |
curl -i -X POST https://forms.hellojade.ai/v1/forms/$TENANT/contact \
-H "X-API-Key: $FORMS_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"form_key":"contact","email":"x@example.invalid"}'
# 400 missing_required → key + form resolve, nothing stored · 401 → key wrong · 403 → tenant mismatchThe prompt
You are posting website form submissions to hellojade's forms endpoint for ONE tenant (a hellojade customer). This is a tenant integration, not a partner one: the key is issued to the customer and is scoped to their tenant slug.
Endpoint: POST https://forms.hellojade.ai/v1/forms/{tenant}/{form_key}
Headers: X-API-Key (from the environment, never in source or logs), Content-Type: application/json
Body: a flat JSON object of the form's fields (name, email, phone, message, plus whatever the form collects). Include page_url and referrer if you have them; campaign attribution (gclid, utm_*) is read from them.
Rules:
- {tenant} in the path must equal the key's tenant, or the answer is 403. Never guess a tenant slug; take it from configuration.
- Never fill a hidden honeypot field. A non-empty top-level "website" field is graded as a bot fill and delivers nothing.
- Both per-IP and per-tenant rate limits apply and answer 429; the numbers are not published. Retry on 5xx and 429 with backoff; never retry a 4xx.
- The ONLY sanctioned production probe is a body that names a real form key and fails required-field validation, for example {"form_key":"contact","email":"x@example.invalid"} → 400 missing_required. That proves the key and form resolve while storing, delivering and emailing nothing. 401 means the key is wrong; 502 or 404 means the form key did not resolve. Never send a plausible name and phone number as a test — real people are emailed on every accepted submission.
- The ops endpoints (/v1/leads, /v1/report, /metrics) are not public; they answer 404 through the edge. Do not build on them.
Done when: the probe above returns 400 missing_required (not 401/403/502), a real submission from the live form is confirmed by the customer's notification email, and the key appears in no log line or committed file.The site is a read surface, too.
Every page carries a schema.org JSON-LD graph, and the whole site ships as one queryable database. No key, no limit beyond ordinary politeness.
- /llms.txt — the agent index: key pages, the knowledge base, and the table schema of the site database.
- /assets/site.data — the entire site as a SQLite file:
page,entity,edge,category. Query it instead of scraping. - /for-agents — how the site is built to be read by an assistant, and the per-agent directive files.
Twelve checks, each observable.
Reproduced from the integration brief (§8) so the agent and the person reviewing its work read the same list. Report the result of each, not a summary.
- Key check returns 422The §1 key check returns 422, not 401, against the key you will ship with.
- A minimal lead returns 202A lead with first_name, last_name and phone returns 202.
- A repeat returns 200 with the same event_idThe same lead sent twice with the same Idempotency-Key returns 202 then 200, with the same event_id, and your system records one send, not two.
- The Idempotency-Key is namespaced and stableIt is prefixed with something only you use and does not change across retries — not a bare integer, not a timestamp, not regenerated per attempt.
- A 422 surfaces every failing fieldA lead missing all three required fields returns 422 and your code reports all three names, not just the first.
- A 503 retries with backoff and loses nothingPoint at a dead host: the lead is retried with growing, jittered backoff and is never dropped.
- A 422 does not retryRetrying an unchanged 422 burns your rate limit and fixes nothing.
- A 429 waits and does not spend an attemptSleep for Retry-After (a floor of one second), grow the wait on repeats, and do not count it as a delivery attempt.
- The key appears in no log, error, trace or commitGrep for it before you call the integration done.
- An unmappable project_area is sent rawUnrecognized values are stored and flagged, never rejected — send what your system calls it.
- event_id is stored and request_id is loggedevent_id against your lead record; request_id on every failure — that is what makes a support question answerable.
- The base URL is https:// and is configurationNothing listens on port 80, and the host must not be a constant in the code.
Questions agents ask
Can my agent test against production?
Only with the sanctioned probe: a real key and an empty JSON body, which returns 422 and stores nothing. Anything with a plausible name and phone number reaches a real person. For a full round trip, ask hellojade for a sandbox key bound to a tenant with no CRM route and no email route.
Should the agent vendor the OpenAPI spec into the repository?
No. Fetch it at build time. The spec, the integration brief and the project vocabulary are served by the platform and change without a deploy on your side; a vendored copy is a copy that drifts.
Which of these APIs does a lead provider use?
The Partner Intake API only. The forms endpoint is for hellojade customers' own websites, and the plan feed is read-only. If you are sending leads to a hellojade customer, you want intake.hellojade.ai and a partner key.
What does a 200 from the intake API mean?
That the Idempotency-Key was already accepted for that customer, and the response carries the original event_id. It is a success, not an error — it is exactly what a retry should produce.
Is there a rate limit on the plan feed?
None is published. Treat it as a build-time fetch or cache it for at least five minutes. Polling it does nothing useful; the plans do not change by the minute.
Need a key, or a sandbox?
Tell us which hellojade customer you are integrating with and what your feed is called. Partner keys, tenant keys and sandbox keys are issued by a person, usually the same day.
Rather look around first? Contact us or All developer resources.