Developers
This is a demo. Nothing is sent to a business or charged. Test requests are kept in a private demo sandbox for about 3 days so status links work, then deleted. There is no API key and no account. The business is fictional.
1. Embed the request form
Paste this where the form should appear. It renders the full request flow (request, optional deposit, confirmation with a status link) in an iframe that resizes itself and works on phones.
<div id="atlas-request"><a href="https://trades-request-demo.vercel.app/?src=embed-fallback">Request service</a></div>
<script src="https://trades-request-demo.vercel.app/embed.js" data-business="summit-peak" async></script>
The script replaces the link inside the <div> with the form. If the script is ever blocked, visitors still get a working link. The shorter form still works too: an empty <div id="atlas-request"></div>, or no div at all (then the form appears right where the script tag is).
Or show a button that opens the form in an overlay:
<div id="atlas-request"><a href="https://trades-request-demo.vercel.app/?src=embed-fallback">Request service</a></div>
<script src="https://trades-request-demo.vercel.app/embed.js" data-business="summit-peak" data-button-mode="modal" data-accent="#b4532a" async></script>
See both on a pretend business site: embed demo.
Options
| Attribute | Values | What it does |
|---|---|---|
data-business | business id, e.g. summit-peak | Which business the form is for. Each business gets its own id (the demo business is summit-peak). An unknown id logs a console error naming it. |
data-accent | hex color, e.g. #b4532a | Brand color for buttons. Text color flips to dark on light colors. |
data-button-mode | modal | Render a button that opens the form in an overlay (Esc, the × or a click outside closes it). |
data-button-text | text | Button label in modal mode. Default "Request service". |
data-target | CSS selector | Mount element. Default #atlas-request. |
When a request is submitted, the mount element fires atlas:request-submitted with event.detail.ref (the reference only, no personal details):
document.addEventListener('atlas:request-submitted', function (e) {
console.log('Request received:', e.detail.ref);
});
How it behaves
- The iframe asks for no special permissions (no camera, payment, etc.) and can't navigate your page. It's sandboxed to scripts, forms, and pop-ups.
- Sizing uses
postMessage. The form only posts to your page's origin. The script only accepts messages from the form's origin and from its own iframe. Messages contain layout info and the request reference, never personal details. - The form pages send
Content-Security-Policy: frame-ancestors *, so any site can embed them (fine for a demo). - The DEMO label stays visible inside the form. Deposits are mocked (test card 4242 4242 4242 4242, or skip), and the status link works the same as on the main demo.
- Requests are stored on the server, so the status link works on any device. On the demo, each browser gets its own private sandbox: the demo owner view shows the requests made from that same browser (on this site or through an embed, if the browser allows the embed to keep storage).
Strict security policies (CSP)
If your site sends a Content-Security-Policy header, add our hosts to two directives (keep your existing values, just append):
script-src https://demo.atlasag.co https://trades-request-demo.vercel.app
frame-src https://demo.atlasag.co https://trades-request-demo.vercel.app
Nothing else is needed. The embed uses no inline <style>, no style="" attributes and no eval, so it works without 'unsafe-inline' or 'unsafe-eval'. If your policy uses default-src or child-src instead of frame-src, add the hosts there.
If the policy isn't updated, the embed fails gracefully:
- Script blocked (
script-src): embed.js can't run, so the plain link inside the<div>stays. This is why the recommended snippet includes it. Your browser console shows its own CSP error. - Frame blocked (
frame-src), or the form doesn't answer within about 4 seconds: the mount shows a styled "Request service" button (using yourdata-accentanddata-button-text) that opens the full form in a new tab. In modal mode, the button opens the new tab instead of an empty overlay. The console shows one warning naming the exact lines to add. If the form answers late (a slow connection), the embedded form comes back automatically.
Test pages served with real CSP headers: frames blocked · script blocked · strict policy that allows our hosts.
2. Request API
POST https://trades-request-demo.vercel.app/api/service-request with a JSON body. GET the same URL for a short description. CORS is open. The spec is at openapi.json (OpenAPI 3.1), and a plain summary for AI assistants is at llms.txt.
curl -X POST https://trades-request-demo.vercel.app/api/service-request \
-H 'Content-Type: application/json' \
-d '{"name":"Jordan Example","phone":"(555) 555-0123","address":"123 Sample St, Sample City, CO",
"service":"water_heater","urgency":"today","description":"No hot water since last night."}'
Required: name, phone, address, service, urgency, description. Optional: email, zip, preferred_time. A success returns 201 with request_id, status_url, demo: true and deposit_required: false. Field problems return 400 with an errors object. Requests are rate-limited per IP. Demo API requests go to a shared demo sandbox and are deleted after about 3 days.
Deposits are never taken through the API. If a job needs one, the business texts the customer a separate link, and the customer approves it themselves.