Developers
The Vexo API
Put Vexo inside your own product. Render designed emails, write posts from facts you supply, and find real businesses of a kind you describe, each read from its own website. You pay per call, from credit you buy up front. There is no subscription, no invoice, and nothing you owe afterwards.
You need an account, and to make a key you need to sign in with Google or LinkedIn and agree to the API terms. An account made with an email and a password has not been verified, and a key can spend money, so it cannot make one.
Quickstart
Sign up, then make a key in the developer portal. Start with a test key: it spends nothing and answers with sample data where a real answer would cost money. Test keys are limited to 2,000 calls a day.
curl https://touchbasetechnologies.com/vexo/api/v1/email/render \
-H "Authorization: Bearer vx_test_..." \
-H "Content-Type: application/json" \
-d '{
"subject": "Welcome, {{first_name}}",
"blocks": [{ "type": "heading", "props": { "text": "Welcome, {{first_name}}" } }],
"variables": { "first_name": "Ada" },
"brand": { "companyName": "Acme" }
}'Authentication
Send the key as a bearer token. Keys look like vx_live_ or vx_test_ followed by 43 characters. A key is shown once, when you make it. Vexo keeps a fingerprint of it, not the key, so it cannot be shown again. Keep it on your server, never in a browser or an app you ship.
- Each key has its own permissions: it can only call the things you ticked when you made it.
- Making a key needs a verified sign-in and agreement to the terms, and is written to your workspace's audit log, along with revoking one.
- Each key can have a monthly spending cap in dollars. Calls past it are refused.
- Revoking a key stops it at once.
Pricing
A credit is one cent, the same credit the Vexo studio spends. Each call takes its price from your balance before any work is done, and gives back whatever was not delivered: a call that fails costs nothing, a company search is charged per company found, and a search that finds none is free. When the balance cannot cover a call, the call is refused with a 402 and does nothing.
| Product | Price | What it does |
|---|---|---|
| email.render | 1 cent per render | Render one of the email templates, or your own blocks, to Outlook-safe HTML and plain text with your brand and merge fields. |
| content.post | 1 cent per post | Write a social post for a topic, platform and voice, grounded in the facts you send and nothing else. |
| leads.company | 5 cents per company | Find a real business of the kind you describe, in the place you name, read from its own website and checked against the kind you asked for. |
Coming next, at these prices: leads.contact (8 cents per contact), campaign.email (1 cent per email), social.publish (2 cents per post), content.image (10 cents per image), video.minute ($2.05 per minute), avatar.minute ($1.94 per minute), avatar.build ($32.50 per presenter). Prices for these are not final until they are released.
The same list is available as JSON from GET https://touchbasetechnologies.com/vexo/api/v1/pricing, with no key.
Render an email
POST /v1/email/render turns one of Vexo's templates, or your own blocks, into table-based HTML that holds up in Outlook, plus a matching plain-text part. List the templates with GET /v1/email/templates, which is free.
POST /v1/email/render
{
"template": "<a key from /v1/email/templates>", // or "blocks": [...] with "subject"
"brand": { "companyName": "Acme", "primaryColor": "#2447d6" },
"variables": { "first_name": "Ada" },
"transactional": false
}
200
{ "subject": "...", "html": "<!doctype html>...", "text": "..." }Merge values are escaped before they reach the page, so a customer's name cannot inject markup into your email.
Write a post
POST /v1/content/posts writes one post for LinkedIn, X, Facebook, Instagram or Threads. It writes from the facts you send and nothing else: a sentence with a number that is in neither your topic nor your facts is cut, and the response says so. It never runs past what the network allows.
POST /v1/content/posts
{
"topic": "We launched instant quotes",
"platform": "linkedin",
"voice": "plain and direct",
"facts": ["Quotes now take under a minute", "Available to all customers from Monday"]
}
200
{ "text": "...", "platform": "linkedin", "characters": 412, "warnings": [] }Find companies
POST /v1/leads/search finds real businesses of any kind you describe, in a US state, city or county. Each comes from open map data, is read from its own website, and is checked against the kind you asked for, so a company that is clearly something else is dropped. You say what you sell, and each result carries a reason it fits that is grounded in the company's own words.
POST /v1/leads/search
{
"kind": "solar installers",
"region": "Austin, Texas",
"count": 5,
"offer": "Quoting software for installers"
}
200
{
"runId": "run_...",
"companies": [{ "id": "...", "name": "...", "website": "https://...", "location": "...", "description": "...", "whyFit": "..." }],
"searchedAs": "Solar installers",
"skipped": { "alreadyHad": 0, "notAFit": 3, "unverified": 1 }
}A call returns at most five companies, because each is a website read and a judgement and a call has to finish in about half a minute. If a call times out on your side, fetch what it found, free, with GET /v1/leads/runs/<runId>. The companies are also saved in your Vexo workspace. Data from OpenStreetMap contributors, ODbL.
Errors
Every error has the same shape, with a code your program can act on and a message a person can read.
{ "error": { "code": "insufficient_credit", "message": "This call costs 0.05 dollars and the balance is 0.03. Top up in the dashboard." } }| 401 | invalid_key | Missing, malformed or revoked key. |
| 402 | insufficient_credit | The balance cannot cover the call. Nothing was done. |
| 402 | key_cap_reached | The call would take the key past its monthly cap. |
| 403 | scope_missing | The key was not given permission for this. |
| 403 | ip_not_allowed | The key may not be used from this address. |
| 400 | invalid_request | Something in the request is missing or wrong. The message says what. |
| 409 | idempotency_replay | That Idempotency-Key was already used. |
| 429 | rate_limited | Too many calls a minute for this key. |
| 502 | upstream_error | The work could not be completed. You were not charged. |
Retries and idempotency
Send an Idempotency-Key header to make a call safe to retry. A key is accepted once: using it again returns a 409 and does nothing, so a retry can never charge you twice. It does not return the first answer, so keep that.
Limits
- 60 calls a minute per key by default, up to 600. Set it when you make the key.
- Requests up to 200 KB. A post topic up to 500 characters. Up to 20 facts.
- Company search is open to the United States for now.
- Prepaid only. Buy credit in the developer portal. There is nothing to pay afterwards.
Ready to build? Sign up and make a test key. Need something that is not here, such as sending campaigns, publishing to social accounts or avatar video? Those are next, and telling us which you need first decides the order.