Skip to documentation
Browse documentation topics
Documentation / Integrations and API

CONNECT YOUR SYSTEMS

Inbound lead API reference

Endpoint, authentication, every field MegaHub reads, batch posting and the responses to expect.

Public documentation9 min read
Explore Integrations and API
Requests in, signed events out: Your server → Posting endpoint → CRM record → Webhook event
Workflow illustration · Requests in, signed events out

Written for whoever is doing the integration at the vendor's end. Everything here is a public endpoint — no user session is involved, because a lead provider cannot hold one.

Endpoints

Method and pathWhat it doesSuccess
POST /api/inbound/{tenantKey}Creates one lead201 Created
POST /api/inbound/{tenantKey}/batchCreates up to the vendor's batch limit207 Multi-Status
POST /api/inbound/{tenantKey}/testValidates a post, writes nothing200 OK

Use the public website at https://megahubcrm.com with the /api/inbound paths above. Copy your exact posting URL from Settings → Lead sources; use the same URL with /test or /batch appended for those endpoints.

Authentication

The posting key goes in an X-Api-Key header. The tenant key goes in the path. Both must match an active lead source, or the request is refused with 401 and errorCode INVALID_POSTING_KEY.

Fields

Send what you have; anything missing is simply absent, and unrecognised top-level fields are kept only in the raw payload. The eighteen standard fields are strings, including year and operable. The optional metadata object preserves short text, numbers and booleans in the lead’s Additional information card.

FieldMaxNotes
name200Full name. Split on the first space into first and last, and only used when those are absent.
first_name100Wins over name.
last_name100Wins over name.
email255Lowercased on the way in. One of email or phone is required.
phone40Any format. Used to match an existing customer. One of email or phone is required.
origin_zip10Digits are extracted and the first five used. This is what resolves the pickup.
destination_zip10As above, for the delivery.
origin_city100Stored on the raw payload, but not used to work out the route.
origin_state40As above.
destination_city100As above.
destination_state40As above.
vehicle_year10A string, not a number. Anything non-numeric is dropped.
vehicle_make60One vehicle per lead through this endpoint.
vehicle_model80Also used to guess the size class — sedan unless the model says otherwise.
operable10no, false, 0, n, inop, inoperable all mean it does not run. Anything else, including an absent field, means it does.
transport_type20Anything containing "enclosed" is enclosed. Everything else, including an absent field, is open.
move_date40When the customer wants it collected. Anything unparseable is dropped rather than guessed.
comments2000Free text. Lands on the lead as a note.
metadata30 fieldsFlat object of short strings, finite numbers or booleans. Appears under Additional information.

Additional information (metadata)

Metadata is optional: omitting it causes no metadata validation error. When supplied and valid, it is saved on the lead. Use application/json and send metadata as a flat object. It works on live, test and batch submissions (inside each lead), including the frontend /api/inbound/{tenantKey} routes. Up to 30 fields; labels match ^[A-Za-z][A-Za-z0-9 _-]{0,39}$, with constructor, prototype and __proto__ reserved. Values are single-line strings up to 160 characters, finite numbers within ±9007199254740991, or booleans. Dates use YYYY-MM-DD strings. Keep leading-zero references as strings. Zero and false are preserved.

Optional field inside each lead payload
json
{ "metadata": { "Reference": "00123", "Budget": 850, "Flexible": true, "Preferred date": "2026-10-20" } }

Null, arrays, nested objects, multiline text and strings starting with { or [ after whitespace are rejected with 400 BAD_REQUEST. Do not send metadata as a JSON-encoded string in form data. Invalid metadata in a batch rejects the request before any leads are created. The test endpoint returns parsed.metadata. Metadata appears after Actions on the lead. Authenticated POST /api/v1/leads also accepts it; PATCH /api/v1/leads/{id} replaces the entire metadata object, omission leaves it unchanged, and {} clears it.

Over-length values are refused with a 400 rather than truncated, so a comments field of 3,000 characters loses the whole post. Leading and trailing whitespace is trimmed from standard string fields. Metadata values retain their types and text.

A single lead

bash
curl -X POST https://megahubcrm.com/api/inbound/YOUR_TENANT_KEY \
  -H "X-Api-Key: lsk_your_posting_key" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Dana",
    "last_name": "Whitfield",
    "email": "dana@example.com",
    "phone": "512-555-0142",
    "origin_zip": "33101",
    "destination_zip": "30301",
    "vehicle_year": "2022",
    "vehicle_make": "Jeep",
    "vehicle_model": "Wrangler",
    "operable": "yes",
    "transport_type": "open",
    "move_date": "2026-03-14",
    "comments": "Flexible by a few days either side."
  }'
201 Created
json
{
  "accepted": true,
  "leadNumber": 10482,
  "leadId": "9f2c...",
  "duplicate": false
}

duplicate: true means the lead was accepted and flagged as looking like one already on file. It is on the board either way — treat it as accepted.

Batches

Batch posting is off until the brokerage enables it for your key and sets how many leads you may send at once. Send a leads array; each entry is accepted or rejected on its own, and the response says which by position. One malformed entry never costs you the rest of the batch.

Request — note the leads wrapper, not a bare array
bash
curl -X POST https://megahubcrm.com/api/inbound/YOUR_TENANT_KEY/batch \
  -H "X-Api-Key: lsk_your_posting_key" \
  -H "Content-Type: application/json" \
  -d '{
    "leads": [
      { "first_name": "Dana",  "phone": "512-555-0142",
        "origin_zip": "33101", "destination_zip": "30301",
        "vehicle_year": "2022", "vehicle_make": "Jeep", "vehicle_model": "Wrangler" },
      { "first_name": "Rudy",  "email": "rudy@example.com",
        "origin_zip": "60601", "destination_zip": "85004",
        "vehicle_year": "2019", "vehicle_make": "Ford", "vehicle_model": "Escape" }
    ]
  }'
207 Multi-Status
json
{
  "accepted": 2,
  "rejected": 1,
  "results": [
    { "index": 0, "accepted": true, "leadNumber": 10483, "leadId": "...", "duplicate": false },
    { "index": 1, "accepted": false, "error": "A lead needs an email or a phone number." },
    { "index": 2, "accepted": true, "leadNumber": 10484, "leadId": "...", "duplicate": true }
  ]
}

Limits

LimitValue
Single and test endpoints300 requests a minute
Batch endpoint60 requests a minute
Leads per batchSet per key by the brokerage; hard ceiling of 200
Leads per hourOptional, set per key. 429 when exceeded.

An hourly limit refuses the whole batch rather than part of it, and the error says how many you have sent this hour. Half a post landing is worse than none: you cannot tell which half without reading every result, and your retry would duplicate whatever got through.

The sandbox

Same authentication, same parsing, same ZIP lookup — and it writes nothing. Post the exact body you intend to send live and read what comes back before you go anywhere near the live path.

bash
curl -X POST https://megahubcrm.com/api/inbound/YOUR_TENANT_KEY/test \
  -H "X-Api-Key: lsk_your_posting_key" \
  -H "Content-Type: application/json" \
  -d '{ "first_name": "Dana", "zipcode": "33101", "dest": "30301" }'
200 OK — the same payload, diagnosed
json
{
  "sandbox": true,
  "wouldCreate": false,
  "source": "Central Leads Network",
  "parsed": {
    "firstName": "Dana", "lastName": null,
    "email": null, "phone": null,
    "transportType": "OPEN", "readyDate": null,
    "notes": null, "vehicle": { "operable": true, "vehicleClass": "SEDAN" }
  },
  "route": null,
  "ignoredFields": ["zipcode", "dest"],
  "warnings": [
    "No email or phone. A real post would be refused: a lead needs at least one.",
    "origin_zip and destination_zip are both needed for a route. City and state are stored but not used to derive one.",
    "No vehicle make or model. The lead is accepted but an agent cannot quote it without ringing back."
  ]
}

ignoredFields is the one to read first. It lists every key you sent that MegaHub does not read — above, a provider sending zipcode and dest would have got a cheerful 201 from the live endpoint and produced a lead with no route on it. wouldCreate tells you whether a real post would be accepted at all; warnings covers everything that would make the lead thin without refusing it.

Errors

Every refusal has this shape
json
{
  "statusCode": 401,
  "errorCode": "INVALID_POSTING_KEY",
  "message": "The posting key is not valid for this endpoint.",
  "requestId": "0daa7354-dd2d-4932-9b72-ca72d6704260"
}

Match on errorCode rather than the message — the wording may be improved, the code will not change. Quote the requestId if you ask support about a specific failed post; it is what finds it in the log.

StatuserrorCodeMeaning
401INVALID_POSTING_KEYTenant key or posting key is wrong, or the source is switched off.
403BATCH_NOT_ENABLEDThis key may only send one lead per request.
400BATCH_TOO_LARGEMore leads than this key is allowed to send at once.
429HOURLY_LIMIT_REACHEDThis key's hourly ceiling. Retry later.

Read next

Need a hand with this step?

Tell us what you tried and the result you received.

Contact support