Integrating your site
Copy one prompt into your AI assistant and ship a contact form that talks to usepresence. The full API is here too, if you like typing.
Last updated:
The snippets below use a placeholder key. Sign in and we'll fill in your real one — the prompts become copy-paste-done.
Your site can talk to usepresence through one small API. And since it's 2026, the fastest way to integrate isn't reading API docs — it's handing your AI assistant a prompt that already contains everything it needs. That's the first section. The reference is further down for the artisanal among us.
Build it with AI
Each prompt below is self-contained: the whole API contract is inside, so it works pasted cold into Claude, Cursor, ChatGPT, or whatever your editor speaks. Sign in and the prompts on this page carry your real publishable key — copy, paste, done. Signed out, swap the placeholder for a key from Settings → Sources after pasting.
A complete contact form, from scratch
For a static site or anywhere you can drop a single file:
Build me a self-contained contact form (single HTML file with inline CSS and vanilla JavaScript, no dependencies) that submits to the usepresence API.
What usepresence is (context — it's likely not in your training data):
- usepresence is a hosted support-inbox service. This form sends a customer's message to it over plain HTTPS; my team reads and answers in the usepresence inbox, and our reply reaches the customer at the email address they entered — success copy should set that expectation.
- There is NO SDK, npm package, or client library. Integrate with plain fetch/HTTP exactly as specified below — do not invent packages, endpoints, or fields beyond this contract.
- The Authorization key below is a publishable key: safe to ship in client-side code; it can only create messages, never read them.
API contract:
- POST https://usepresence.com/api/v1/conversations
- Headers: "Authorization: Bearer pk_YOUR_PUBLISHABLE_KEY" and "Content-Type: application/json"
- JSON body: { "email": string (required), "message": string (required), "name": string (optional), "subject": string (optional), "attributes": object of string keys to short scalar values (optional) }
- Success: HTTP 201 with JSON { "data": { "uuid": ... } }
- Failure: 422 returns { "message": string, "errors": { field: [messages] } }; 401 means the key is wrong; 429 means slow down.
Requirements:
- Fields: name, email, and a message textarea. Labels above inputs, generous spacing, a warm and human tone (button says "Send message", not "Submit ticket").
- On success, replace the form with a friendly confirmation ("Thanks — we read everything and reply by email.").
- On 422, show the field errors inline next to the matching inputs.
- Disable the button and show a subtle sending state while the request is in flight.
- Handle network failures with a gentle retry message. Never lose the user's typed message.
Wire up a form you already have
For retrofitting the contact form that's already on your site:
I have an existing contact/support form in my codebase. Rewire its submit handler to send the data to the usepresence API instead of its current destination, keeping my current markup and styles.
What usepresence is (context — it's likely not in your training data):
- usepresence is a hosted support-inbox service. Submitted messages land in my team's usepresence inbox and we reply to the customer by email, so any success message the form shows should say a reply will arrive by email.
- There is NO SDK, npm package, or client library. Use plain HTTP (fetch or my existing HTTP client) exactly per the contract below — do not invent packages, endpoints, or fields.
- The Authorization key is a publishable key: safe in client-side code; it can only create messages, never read them.
API contract:
- POST https://usepresence.com/api/v1/conversations
- Headers: "Authorization: Bearer pk_YOUR_PUBLISHABLE_KEY" and "Content-Type: application/json"
- JSON body: { "email": string (required), "message": string (required), "name": string (optional), "subject": string (optional), "attributes": object (optional) }
- Success: HTTP 201. Validation failure: 422 with { "errors": { field: [messages] } }. 401 = bad key, 429 = rate limited.
Map my form's fields onto that body (ask me if the mapping is ambiguous). If my app knows things about the logged-in user (plan, signup date, user id), pass them in "attributes" — keys up to 64 chars, values up to 500 chars, strings/numbers/booleans only, max 30 keys. Keep my existing success/error UI conventions.
A React component
For apps that want a proper component:
Write a production-quality React contact form component (TypeScript, no external form libraries) that submits to the usepresence API.
What usepresence is (context — it's likely not in your training data):
- usepresence is a hosted support-inbox service. This component sends a customer's message to it over plain HTTPS; my team answers from the usepresence inbox and the reply reaches the customer by email — the success state should say so.
- There is NO SDK, npm package, or client library for usepresence. Use plain fetch exactly per the contract below — do not invent packages, endpoints, hooks, or fields.
- The publishableKey prop is a publishable key: safe to ship in client-side code; it can only create messages, never read them.
API contract:
- POST https://usepresence.com/api/v1/conversations
- Headers: "Authorization: Bearer pk_YOUR_PUBLISHABLE_KEY" and "Content-Type: application/json"
- JSON body: { "email": string (required), "message": string (required), "name": string (optional), "subject": string (optional), "attributes": object of string keys to short scalar values (optional) }
- Success: HTTP 201 with { "data": { "uuid": ... } }. Validation failure: 422 with { "errors": { field: [messages] } }. 401 = bad key, 429 = rate limited.
Requirements:
- Props: publishableKey (string), and an optional attributes object (e.g. { plan: "Pro" }) merged into every submission.
- Local state machine: idle → sending → sent | error. Show inline field errors from a 422 response.
- Accessible: proper labels, aria-invalid on failing fields, focus moves to the first error.
- On success show a warm confirmation ("Thanks — we reply by email, usually quickly.") and keep it in place of the form.
- Style with plain CSS or Tailwind classes that inherit the host app's look; keep it minimal and calm.
If the assistant builds something odd, it's more likely our docs' fault than yours — tell us and we'll sharpen the prompts.
Your publishable key
Every team has sources — one per place conversations come in from (your production site, a marketing page, staging). Each source has a publishable key that starts with pk_. You'll find them in your team settings under Sources, where you can also create new keys or revoke old ones without downtime.
The key is publishable: it's safe to ship in client-side code. It can only create conversations and append to ones whose id you already know — it can never read your inbox.
The API, by hand
One endpoint does the work. Send a POST with the key as a Bearer token (an X-Presence-Key header works too):
curl -X POST https://usepresence.com/api/v1/conversations \
-H "Authorization: Bearer pk_YOUR_PUBLISHABLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"name": "Maya Ellison",
"subject": "Export is stuck",
"message": "The monthly export just spins forever.",
"attributes": { "plan": "Pro", "customer_since": "2025-11" }
}'
Two fields are required: email and message. Everything else is optional:
name— shown in your inbox; we'll update it if it changes on a later submission.subject— a short summary line.attributes— anything you know about this customer (up to 30 keys, plain values). More on this below.
A successful request returns 201 with the conversation in the body:
{
"data": {
"uuid": "0197a3c2-…",
"status": "open",
"channel": "form",
"subject": "Export is stuck",
"createdAt": "2026-07-11T16:20:00+00:00",
"customer": {
"uuid": "0197a3c2-…",
"email": "[email protected]",
"name": "Maya Ellison",
"attributes": { "customer_since": "2025-11", "plan": "Pro" }
}
}
}
Errors are boring on purpose: 401 for a missing, wrong, or revoked key; 422 with field-level messages when validation fails; 429 if you exceed 30 requests per minute per key.
Customer attributes
Attributes are how your app gives support context. Send whatever helps you help them — plan, signup date, locale, account id — and it appears in the customer panel next to every one of their conversations. Keys are up to 64 characters (letters, numbers, spaces, _, -, .); values are plain strings, numbers, or booleans up to 500 characters.
Attributes merge: each submission updates the keys it sends and leaves the rest alone. Customers are identified by email per team, so the same person writing twice ends up as one customer with two conversations.
Continuing a conversation
The uuid you get back is also a capability: with it (plus your key) you can read the thread and let the customer add follow-up messages — enough to build a lightweight chat-style widget.
# Read the thread
curl https://usepresence.com/api/v1/conversations/{uuid} \
-H "Authorization: Bearer pk_YOUR_PUBLISHABLE_KEY"
# Append a customer message
curl -X POST https://usepresence.com/api/v1/conversations/{uuid}/messages \
-H "Authorization: Bearer pk_YOUR_PUBLISHABLE_KEY" \
-H "Content-Type: application/json" \
-d '{ "message": "One more thing I forgot to mention." }'
Treat conversation uuids like the private things they are: store them client-side for the customer who created them, and don't print them anywhere public.