Customer Portal API
Areas
The 1Tool API lets your own software read and change all data of your 1Tool tenant: contacts, projects, quotes, invoices, appointments and much more. This page takes you to your first call in a few minutes. On the left you find every endpoint, ordered by business area.
Getting started
Address of the API: https://api.1tool.cloud/v1 – protected by a web application firewall. Use it for every new integration; the examples below use the address of the page you are reading.
1. Get a token
Every call needs a bearer token. You get one with the email address and password of a 1Tool user:
curl -X POST https://api.1tool.cloud/v1/auth/user \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"email": "anna.berger@example.at", "password": "…"}'
If the user has two-factor authentication turned on, also send code with the code from the authenticator app. The response contains the token in api_token.
- A token is valid for one week. After that, calls answer
401; sign in again for a new one. Store the token, not the password, and never in a browser or app that customers can read. - For a permanent integration, create a dedicated user with exactly the permissions the integration needs, so you can block it on its own and see in the log what it did.
- End a token when you no longer need it: see Sessions and devices in the area Sign-in and account.
2. Make your first call
curl "https://api.1tool.cloud/v1/contacts?per_page=5" \
-H "Accept: application/json" \
-H "Authorization: Bearer <your token>"
You see exactly the data this user may also see in 1Tool. Permissions apply to the API just as they do in the app.
3. Find the right endpoint
On the left are the business areas, inside them the resources, and below those the individual calls. Search (Ctrl or Cmd + K) finds endpoints by name, path or description. For every call you can copy a code sample on the right or try it out directly with your token.
Rules that apply to every endpoint
Headers
| Header | When | Value |
|---|---|---|
Authorization |
always, except for sign-in | Bearer <token> |
Accept |
recommended | application/json |
X-Tenant |
only for contact sign-ins and public endpoints | the tenant's UUID. Not needed with a user token: the token knows its tenant. |
Paging through lists
Lists come in pages as { "data": [...], "links": {...}, "meta": {...} }. Follow links.next until it is empty.
per_pagesets the page size, at most 1000. Larger values are cut to 1000 without an error; the size actually used is inmeta.per_page, the total inmeta.total.- For large tables, page with
sort=id&filter[id_after]=<last id>instead of high page numbers. Many large pages in a row are rate-limited per tenant. skip_total=1skips counting all matches when you do not need the total.
Filtering
Filters are written filter[<name>]=<value>. The filters an endpoint knows are listed with its parameters; an unknown filter is rejected with an error that names the allowed ones. Separate several values with commas: filter[id]=1,2,3.
Sorting
sort=<column> ascending, sort=-<column> descending. Careful: an unknown sort column is not rejected: the list comes back in its default order, and the response header X-Ignored-Sort names the column that was ignored. Check the name against the list on the endpoint.
Loading related data
include=contactGroups,assignedUsers loads related records in the same response. The allowed names are listed with the endpoint's include parameter.
Dates and times
Dates are written as Y-m-d, date and time as Y-m-d H:i:s, without a time zone: they are local time in Europe/Vienna, in filters, in request bodies and in responses (a few older fields return d.m.Y).
| Value | Means | In UTC |
|---|---|---|
2026-07-01 14:00:00 |
14:00 summer time (CEST) | 2026-07-01T12:00:00Z |
2026-12-01 14:00:00 |
14:00 winter time (CET) | 2026-12-01T13:00:00Z |
Convert to Vienna time before you send a value, and treat values you read as Vienna time. Around the clock change, prefer whole days: on the last Sunday in October the hour from 02:00 to 03:00 happens twice.
Rate limits
| What | Limit |
|---|---|
| All calls of one user or contact | 1,500 per minute (some tenants have a lower budget) |
Sending email (emails/send) |
100 per minute |
Bulk reads (per_page of 500 or more, beyond page 20) |
per tenant 10 pages per minute from 07:00 to 19:00, 30 at night, at most 2 in parallel |
Above a limit the API answers 429 Too Many Requests with a Retry-After header in seconds. Wait that long, then continue; on repeated 429s, double the wait each time. For whole tables, page with filter[id_after] (see above) and read pages one after another, not in parallel.
Custom fields
Tenant-specific fields are called custom_fields_0.<field> (for all records of a type) or custom_fields_<n>.<field> (for one subtype only, for example one appointment category). Load them with include=custom_fields_0, write them with {"custom_fields_0.i_1": 123}. GET https://api.1tool.cloud/v1/custom-fields/<area> returns the display names.
For AI systems
Claude and ChatGPT can use 1Tool directly through our MCP connector. Every call has a fixed operationId (for example contacts.list, invoices.get) by which an agent can name it unambiguously.
- llms.txt lists every resource with a link to its own small OpenAPI file. An AI system loads only the resources it needs instead of the whole specification.
- This guide as Markdown for pasting into a prompt or a knowledge base.
- Each resource page offers its own OpenAPI download; the full specification is linked at the top left.