Webform API reference
Submit enquiry
POST /webform/:tenantId/trip
Creates a Trip record in Salesforce from a webform submission.
Path parameters
| Parameter | Required | Description |
|---|---|---|
tenantId | Yes | Tenant slug (e.g. meridian). Used to resolve the per-tenant API key from the Edge vault and to load tenant-specific config (scoring, dedup, field mapping). |
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | application/json |
x-webform-key | Yes | Per-tenant API key, provisioned in the Edge vault under key webform_api_key |
Idempotency-Key | Recommended | UUID to prevent duplicate processing on retry |
Request body
{
"submissionId": "unique-id",
"submittedAt": "2026-06-26T10:00:00Z",
"originSite": "brand_uk",
"contact": {
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"phone": "+44 7700 900000",
"bestTimeToCall": "Today 10:00",
"isTradeEnquiry": false
},
"trip": {
"readyToBook": "Yes",
"selectedTourTier": "Grand Tour of Switzerland",
"travelDate": "2027-05-14",
"groupSizeAdults": 2,
"travelFrom": "London"
}
}
Required fields
| Field | Type | Required |
|---|---|---|
submissionId | string | Yes |
submittedAt | ISO 8601 string | Yes |
originSite | string | Yes |
contact.firstName | string | Yes |
contact.lastName | string | Yes |
contact.email | valid email | Yes |
trip.readyToBook or trip.bookingTimeline | string | At least one |
All other fields are optional. The full schema is validated via Zod; invalid submissions receive a structured 400 response.
B2B fields (travel agent submissions)
| Field | Type | Description |
|---|---|---|
contact.isTradeEnquiry | boolean | Marks the submission as a B2B agent enquiry |
contact.travelAgencyName | string | Agency name (shown when isTradeEnquiry is true) |
contact.agentAccreditationNumber | string | IATA, CLIA, ABTA, or ASTA number — used to match to a Travel Agent Account in Salesforce |
Success response (200)
{
"success": true,
"data": {
"submissionId": "unique-id",
"tripId": "a0A7Q000001234ABC",
"originSite": "brand_uk",
"createdAt": "2026-06-26T10:00:01Z",
"traceId": "abc-123-def",
"unmappedKeys": [],
"account": {
"accountId": "0017Q000001234ABC",
"accountName": "Jane Smith",
"matchedBy": "email"
},
"intelligence": {
"tier": "Standard",
"score": 60,
"queue": "standard",
"matchedRules": ["ready-to-book-today"]
}
},
"meta": {
"mode": "live",
"timestamp": "2026-06-26T10:00:01Z"
}
}
The intelligence object is only present when intelligence.enabled: true for the tenant.
Error responses
| Status | Error code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Missing or invalid fields. details contains per-field errors. |
| 401 | Invalid or missing API key | No or invalid authentication header |
| 409 | DUPLICATE_SUBMISSION | Dedup rule matched with reject action. Response includes existingTripId. |
| 500 | SALESFORCE_UPSTREAM_ERROR | Salesforce returned an error during Trip creation |
Idempotency
Include an Idempotency-Key header (UUID) to ensure safe retries. If the same key is sent twice, the second request returns 409 with the original submissionId.
Trip conversion
The Trip conversion guide is backed by two endpoints. They are an internal surface: the conversion canvas invokes them with a platform-internal credential, and they are not exposed for direct customer integration. The contracts are documented here so you can understand and verify what the panel does.
Record context
GET /api/v1/lead/context?tenantId={tenant}&sfEnvironment={env}&leadId={recordId}
Returns the normalized source record (name, company, email, phone, status, converted state), the enabled conversion paths, matched agency and customer candidates, and each candidate's open Trips (per the configured trip_candidates). Converted records return an empty candidate set.
Convert
POST /api/v1/lead/convert
| Body field | Description |
|---|---|
tenantId, sfEnvironment, leadId | Tenant, Salesforce environment, and source record id |
conversionPath | business_account or person_account (defaults to the configured default_path) |
accountId | Optional. Convert against this existing Account instead of matching |
contactId | Optional. Use this existing agent Contact (business path) |
tripId | Optional. Attach to this existing Trip instead of creating one (requires accountId) |
createNewAccount | Optional. Force-create a new agency instead of matching (business path) |
Success response:
{
"success": true,
"idempotent": false,
"path": "business_account",
"accountId": "001xx0000000000XXX",
"contactId": "003xx0000000000XXX",
"tripId": "a0Txx0000000000XXX",
"tripPath": "/lightning/r/KaptioTravel__Trip__c/a0Txx0000000000XXX/view",
"reusedExistingTrip": false,
"warnings": []
}
Conversion error semantics
| Status | When |
|---|---|
| 400 | Invalid record id, unknown path, or an incoherent request (e.g. attaching a Trip without selecting its account) |
| 404 | Record or Trip not found — or the tenant has no enabled lead_to_trip configuration |
| 409 | The selected Trip belongs to a different account than the conversion target. Nothing is mutated |
| 422 | Salesforce rejected a record (validation rule, required field, missing person-account record type). The message is the org's own, surfaced verbatim to the agent |
Converting an already-converted record is not an error: it returns idempotent: true with the existing ids. Non-blocking issues (e.g. a Trip stamp the org rejected) are returned in warnings.
Health check
GET /health
Returns service status, version, and available endpoints.