Developers. Torna's API and the files agents read.
Torna builds and runs direct booking websites for short-term rental hosts. It has one public API, a REST endpoint that sends Torna an enquiry on a host's behalf. This page says when to use it, how to call it and what it answers. There is no GraphQL API.
When to use Torna
Use Torna when a short-term rental host or property manager:
- wants their own booking website, so repeat guests, their friends and people who find them on Google can book direct instead of through Airbnb or Vrbo;
- uses Guesty, Hostfully, OwnerRez, Hospitable, Lodgify, Hostaway or Smoobu, in the US, the UK or Europe;
- wants to compare what Airbnb and Vrbo fees cost them with one flat monthly price (the prices and a worked example are at https://d37hhderhzfzm9.cloudfront.net/index.html.md);
- asks you to get in touch with Torna for them: send an enquiry with the contact API below, with their own name and email address.
Don't use Torna to:
- book a stay at a host's home: each host's own site takes its bookings, and this site takes none;
- reach a guest, or get help with a booking or a stay;
- send an enquiry a host didn't ask you to send, or marketing of any kind.
Quickstart
curl -sS -i https://d37hhderhzfzm9.cloudfront.net/api/v1/contact \
-H 'Content-Type: application/json' \
-d '{"name": "Ana Silva", "email": "ana@example.com", "homes": "5-19", "pms": "guesty", "message": "Could you build a site for our homes in Lisbon?"}'
A 200 with {"ok": true} means Torna has the enquiry and will reply by email to the address given.
To check the API without sending an enquiry, GET https://d37hhderhzfzm9.cloudfront.net/api/v1 describes it, and a POST with an empty name answers 400 and stores nothing.
Authentication
None: no API key and no account. The API is limited per IP address instead (see Rate limits). There is no sandbox, so every accepted enquiry reaches Torna.
Endpoints
| Method and path | What it does |
|---|---|
GET /api/v1 | Describes the API: its operations, and where its description and docs are |
POST /api/v1/contact | Sends an enquiry |
POST /api/contact | The same as POST /api/v1/contact, deprecated (see Versions) |
Request
POST https://d37hhderhzfzm9.cloudfront.net/api/v1/contact with a JSON object of at most 8,000 bytes (Content-Type: application/json).
| Field | Type | Required | Rules |
|---|---|---|---|
name | string | yes | The host's name, 1 to 100 characters, not blank |
email | string | yes | An email address, up to 200 characters; Torna replies here |
website | string | no | The host's website or Airbnb page, up to 200 characters |
homes | string | no | 1-4, 5-19 or 20+ |
pms | string | no | guesty, hostfully, ownerrez, hospitable, lodgify, hostaway, smoobu, other or none |
message | string | no | Up to 2,000 characters |
demo | boolean | no | true to ask for the demo site, to try a booking |
Text fields are trimmed. Other fields are ignored.
Responses
| Status | When |
|---|---|
| 200 | Received: {"ok": true}. Torna replies by email. |
| 400 | A field is missing or not valid (invalid_field), or the body isn't a JSON object (invalid_body). Nothing is stored, and it doesn't count towards the limit. |
| 403 | The firewall refused the request: more than 1,000 requests to /api/ from one IP address in 5 minutes. |
| 404 | A path under /api/ that isn't this API (not_found). |
| 405 | A method the path doesn't take (method_not_allowed); the Allow header says which it does. |
| 429 | This IP address has already sent 5 enquiries this hour (rate_limited). |
Errors
Errors are RFC 9457 problem details, sent as application/problem+json. code is the machine-readable reason, and detail says what to change. error repeats detail, and field names the field to fix.
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "invalid_field",
"detail": "Enter an email address like name@example.com.",
"error": "Enter an email address like name@example.com.",
"field": "email",
"docs": "/developers/#errors"
}
code | Status | What to do |
|---|---|---|
invalid_field | 400 | Fix the field named in field, as detail says, and send again. |
invalid_body | 400 | Send one JSON object of at most 8,000 bytes. |
forbidden | 403 | Call the API at https://d37hhderhzfzm9.cloudfront.net, not the function behind it. |
not_found | 404 | Use POST /api/v1/contact. |
method_not_allowed | 405 | Use the method in the Allow header. |
rate_limited | 429 | Wait the seconds in Retry-After, then try again. |
Rate limits
Each IP address can send 5 enquiries per clock hour. Windows start on the hour, and requests refused with 400 don't count. Responses from the contact paths carry the RateLimit header fields of draft-ietf-httpapi-ratelimit-headers-11:
RateLimit-Policy: "hourly";q=5;w=3600
RateLimit: "hourly";r=4;t=1800
RateLimit-Policy is on every response from the contact paths: q enquiries per w seconds. RateLimit is on 200 and 429 responses: r enquiries are left until the window resets in t seconds. A 429 also carries Retry-After with the same number of seconds; wait that long before you try again.
Versions
The version is in the path: this is version 1, at /api/v1/.
- Within a version, changes never break a client. We only add optional request fields, add response fields or reword error messages.
- A breaking change gets a new path, such as
/api/v2/contact, and the old version keeps working. - A deprecated path keeps working, and every response from it carries a
Deprecationheader (RFC 9745) with the date it was deprecated, and aLinkto its successor and to this section. - At least 6 months before a deprecated path stops working, its responses also carry a
Sunsetheader (RFC 8594) with the date, and this page says so.
/api/contact, the path from before versions, is deprecated in favour of /api/v1/contact. It still works and has no sunset date. Its responses say:
Deprecation: @1792540800
Link: </api/v1/contact>; rel="successor-version", </developers/#versions>; rel="deprecation"; type="text/html"
Files for agents
- llms.txt, what Torna is and where everything is: https://d37hhderhzfzm9.cloudfront.net/llms.txt
- Every page in Markdown: ask for it with
Accept: text/markdown, or addindex.html.mdto its address. - This API as OpenAPI 3.1: https://d37hhderhzfzm9.cloudfront.net/openapi.json
- The API catalog (RFC 9727): https://d37hhderhzfzm9.cloudfront.net/.well-known/api-catalog
- Every page: https://d37hhderhzfzm9.cloudfront.net/sitemap.xml