For agents and integrators
Machine intake contract
The same intake a person taps through at /contact/, as plain JSON. No JavaScript, no form scraping. Everything an agent submits lands in the same Project File a human submission does and is reviewed by the same licensed engineer.
Ground rules
Prices in the catalog are in USD and are either fixed, a typical range, or quote-required. When quoteRequired is true, no price exists until a licensed PE scopes the request after intake; do not infer one. Geography is explicit: each service lists the state codes where the firm holds licensure. Payment buys a defined engineering service, never a promised determination or seal.
1. Discover
GET /catalog.json on the site (static, always available) or GET https://oasisengineering.com/api/catalog (live). Each service has id, pricing, geography, turnaround, quoteRequired and an intake path. Services under the EngineerLetters brand hand off to their own funnel URL; everything else uses the flow below.
2. Read the interview
GET https://oasisengineering.com/api/interview/front-door returns the step list: each step has an id, a kind (choice, multi, yesno, text, story, contact, address, upload, info, review) and, for choices, the allowed options[].value. Branching is driven by answers; the first step is need.
3. Create a draft
POST https://oasisengineering.com/api/draft
Content-Type: application/json
{ "interviewId": "front-door", "attribution": { "ref": "my-agent" } }
→ { "id": "01J…", "token": "…", "interviewId": "front-door", "interviewVersion": 3 } Keep the token; it is the bearer credential for this draft only.
4. Answer
PATCH https://oasisengineering.com/api/draft/{id}
Authorization: Bearer {token}
{ "currentStepId": "letter.who",
"answers": {
"need": "letter",
"letter.situation": "mh",
"letter.contact": { "name": "…", "email": "…", "phone": "+1…" },
"letter.address": { "street": "…", "city": "…", "state": "FL", "zip": "…" }
} } Answer keys are step ids; values are option values, booleans, strings, or the contact/address objects shown. Send the whole answers object each time (idempotent). A draft with an email becomes a lead; abandoned drafts get one follow-up.
5. Upload evidence
POST https://oasisengineering.com/api/draft/{id}/files
Authorization: Bearer {token}
multipart/form-data; file=<binary> (jpeg, png, heic, webp, pdf, mp4, mov, dxf · ≤25 MB · ≤12 files) 6. Submit
POST https://oasisengineering.com/api/draft/{id}/submit
Authorization: Bearer {token}
{}
→ { "projectFileId": "OE-XXXXXX", "next": { "kind": "status", "url": "/thanks/?id=…" } }
or { "next": { "kind": "redirect", "url": "https://mh.engineerletters.co/…" } } // brand funnel handoff When bot protection is enabled, submit requires a Turnstile token from a browser session; agents without one receive 400. Registered agents can request an API key at info@oasisengineering.com and will then pass X-Agent-Key instead.
7. Track
GET https://oasisengineering.com/api/status/{projectFileId}
→ { "id": "OE-…", "service": "letter.mh-foundation", "stage": "engineer-review" | "quoted" | "approved" | "paid" | …,
"missing": [ { "key": "photos", "label": "…", "required": true } ],
"payUrl": "https://checkout.stripe.com/…" // only once a PE has approved a quote Digital products
POST https://oasisengineering.com/api/checkout/product { "slug": "<catalog product slug>", "email": "buyer@…" }
→ { "url": "https://checkout.stripe.com/…" } // server-priced; the slug is the only input Fulfilment is automatic after payment: signed download links by email, also readable at GET /api/checkout/session/:id.
What is not exposed
Pricing logic, routing rules and eligibility verdicts run server-side and are not an API; the model used for file extraction never decides money, identity or eligibility. There is no endpoint that returns a seal or a determination. If that is what you need, the answer is an engineer, and the intake above is how you reach one.