API Documentation
Welcome to the API documentation. This guide will help you understand how to integrate the API into your applications using our REST interface.
Base URL
Direct all API requests to: /api/v1
Quick Start
Start using the API in just 5 minutes. Follow these steps:
1. Generate an API Key
Log in to the application and go to Settings → API Keys. Click "New Key" and choose permissions.
2. Make Your First Request
Test the connection by getting a list of projects:
curl -X GET "/api/v1/projects" \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Accept: application/json"
3. Explore the Response
A successful response will look like this:
{
"status": "ok",
"data": [
{
"id": 1,
"name": "My first project",
"description": "Project description",
"created_at": "2024-01-15T10:30:00Z"
}
]
}
Authentication
The API uses Bearer tokens for authentication. Every request must include a valid API key.
Authentication Methods
1. Authorization Header (recommended)
Authorization: Bearer sk_live_your_api_key
2. Query Parameter (for quick tests)
/api/v1/projects?key=sk_live_your_api_key
Key Types
sk_live_*- Production key, works with real datask_test_*- Test key (coming soon)
Permissions (Scopes)
| Scope | Description |
|---|---|
read |
Read data (GET requests) |
write |
Create and update (POST, PUT) |
delete |
Delete resources (DELETE) |
admin |
Administrative operations |
Rate Limits
The API has limits set to ensure fair access for all users.
| Plan | Limit | Window |
|---|---|---|
| Default | 1,000 requests | per hour |
Rate Limit Headers
Each response contains information about the current limit status:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 2024-01-15T11:00:00Z
External Authentication (SSO-lite)
Use CML.SEMTIX as a central credential store. A 3rd-party application
forwards the user's email + password to this API and gets back the
matching user_id if (and only if) the credentials are
valid. No JWT, no session, no cookies — it is a pure
server-to-server check that another service can plug into
its own login screen.
How it differs from /api/auth/login
/api/auth/login— used by the CML.SEMTIX frontend. Issues JWT, refresh token, session, runs device-approval and IP-whitelist checks. Public endpoint./api/v1/auth/verify— used by external services. Requires an API key. Returns only the user id + minimal profile. No tokens issued. Skips device/IP checks because the caller is a server, not a browser.
Flow
- Generate an API key in CML.SEMTIX (Settings → API Keys). Give it the
readscope. Store the key (sk_live_…) as a secret in your other application. - User logs in on the 3rd-party app — your login form collects e-mail + password as usual.
- Your backend forwards them to
POST https://<cmlsemtix-host>/api/v1/auth/verifywith the API key in theAuthorization: Bearerheader. - 200 OK → use the returned
user_idto look up / create your local user record and start your own session. 401 → reject the login. Show one generic error to the user (never reveal whether the e-mail exists).
Headers
| Header | Value |
|---|---|
Authorization | Bearer sk_live_your_api_key |
Content-Type | application/json |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | yes | User's e-mail (case-insensitive) |
password | string | yes | Plain password — server compares against bcrypt hash |
allowed_domains | string[] or CSV string | no | If set, only users whose e-mail domain is on this list can pass. Example: ["dobahna.cz"] or "dobahna.cz,semtix.cz". Lets you block customers / external clients from logging into an internal tool. |
allowed_roles | string[] or CSV string | no | If set, only users with one of these roles can pass. Roles in the system: superadmin, admin, user. Example: ["superadmin"]. |
allowed_domains and allowed_roles are
AND-combined when both are present (user must
satisfy both). A rejected user always sees the same generic
401 Invalid credentials — they cannot tell whether
the password was wrong or whether they are simply out of scope.
Examples of filter use
- Only employees —
"allowed_domains": ["dobahna.cz"] - Only platform super-admins —
"allowed_roles": ["superadmin"] - Only internal super-admins (both must hold) —
"allowed_domains": ["dobahna.cz"], "allowed_roles": ["superadmin"] - Admins and super-admins from two domains —
"allowed_domains": ["dobahna.cz","semtix.cz"], "allowed_roles": ["admin","superadmin"]
The filters are sent by your backend (alongside the API key) — the end user never sees them and cannot tamper with them. Hard-code them in the integrating application's config, not in the login form.
Example — curl
curl -X POST https://your-cmlsemtix-host/api/v1/auth/verify \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"email":"jana@dobahna.cz",
"password":"hunter2",
"allowed_domains":["dobahna.cz"],
"allowed_roles":["superadmin","admin"]
}'
Example — PHP
$ch = curl_init('https://your-cmlsemtix-host/api/v1/auth/verify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CMLSEMTIX_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'email' => $email,
'password' => $password,
// Lock this integration to in-house staff only:
'allowed_domains' => ['dobahna.cz'],
'allowed_roles' => ['superadmin', 'admin'],
]),
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code === 200) {
$data = json_decode($body, true);
$remoteUserId = $data['user_id']; // log them in locally
} else {
// 401 — invalid credentials; show generic error
}
Example — Node.js
const res = await fetch('https://your-cmlsemtix-host/api/v1/auth/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CMLSEMTIX_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password,
// Restrict to staff super-admins of dobahna.cz:
allowed_domains: ['dobahna.cz'],
allowed_roles: ['superadmin'],
}),
});
if (res.ok) {
const { user_id, email, name, role } = await res.json();
// start local session keyed on user_id
} else {
// 401 — reject login
}
Success response (200)
{
"user_id": 42,
"email": "jana@example.com",
"name": "Jana Nováková",
"role": "user"
}
Failure response (401)
Returned identically when:
- e-mail is unknown,
- password is wrong,
- account is deactivated (
is_active = 0) or has login disabled (can_login = 0), - e-mail's domain is not in
allowed_domains, - user's role is not in
allowed_roles.
The caller cannot distinguish between these cases — by design,
to prevent account enumeration and to keep your filter rules
opaque to attackers. The real reason is recorded in the
CML.SEMTIX auth_logs for audit.
{ "error": "Invalid credentials" }
Security checklist for the integrating side
- HTTPS only. The user's password travels in the request body — never call this endpoint over plain HTTP.
- One API key per integrating app. If a key leaks you can revoke just that one, and the request log tells you who was using it.
- Treat the API key as a secret. Store it in env vars / a secret manager, never in client-side code or a public repo.
- Rate-limit your own login form. The API has its own per-key limit (see Rate Limits), but you should also throttle by IP / e-mail on your side so a leaked key can't be turned into a bruteforce farm.
- Generic error messages. Always show "invalid credentials" to the end user — never echo the API response verbatim.
- Log on your side too. CML.SEMTIX logs every verify attempt (
external_verify_ok/external_verify_failed), but you'll also want your own audit trail for compliance.
When NOT to use this endpoint
If you need users to consent to a 3rd-party app accessing their data (i.e. the user does not trust the app with their CML.SEMTIX password), use OAuth-style flows instead — this endpoint is intended for internal tools where the same organisation owns both sides.
Error Codes
The API uses standard HTTP status codes:
| Code | Meaning | Description |
|---|---|---|
200 |
OK | Request completed successfully |
201 |
Created | Resource was created |
400 |
Bad Request | Invalid request (missing parameters) |
401 |
Unauthorized | Missing or invalid API key |
403 |
Forbidden | Insufficient permissions |
404 |
Not Found | Resource does not exist |
429 |
Too Many Requests | Rate limit exceeded |
500 |
Server Error | Internal server error |
Data Hierarchy
Data is hierarchical. Understanding this structure is key for proper API usage.
- Project - Top level. Contains everything else.
- Todolist - Category/list within a project (e.g., "Backend", "Frontend").
- Todo - The actual task. Always belongs to a todolist.
- Comment - Discussion on a task. Always belongs to a todo.
Practical Example
Want to create a task? First you need a todolist_id.
You get that from a project. So the workflow is: Project → Todolist → Todo.
Projects
List Projects
Returns a list of all projects the user has access to.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| archived | boolean | optional Include archived projects |
Get Project
Returns detailed information about a specific project.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
Lookup Project by Variable
Finds a project by any of its variables (not just the id) and returns its complete configuration in a single call: core fields, billing model, all plain variables, timed variables (grouped periods), members, and every todolist with its todos.
Pass the variable as a query parameter named after the variable key,
e.g. ?google_ads_id=392-963-5599. Any project variable key
works (google_analytics_id, gtm_id, custom keys, …).
Matching is exact on both key and value.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| <var_key> | string | required The variable key as the
parameter name, its value as the parameter value
(e.g. google_ads_id=392-963-5599). |
| var_key / var_value | string | alternative Explicit form when the key contains characters awkward as a param name. |
Example Request
curl -X GET "/api/v1/projects/lookup?google_ads_id=392-963-5599" \
-H "Authorization: Bearer sk_live_your_api_key"
Example Response
{
"data": {
"matched_by": { "var_key": "google_ads_id", "var_value": "392-963-5599" },
"multiple_matches": null,
"project": { "id": 53, "name": "...", "billing_type": "tm_project", ... },
"billing": {
"billing_type": "tm_project",
"budget_type": "total_hours",
"budget_hours": 40,
"budget_amount": null,
"hourly_rate": 1200,
"disallow_over_budget": false,
"settings": { ... }
},
"variables": [
{ "var_key": "google_ads_id", "var_value": "392-963-5599", "template_label": "Google Ads ID", ... }
],
"timed_variables": [
{ "template_id": 2, "label": "Google Ads rozpočet", "currency": "CZK",
"periods": [ { "value": 65000, "valid_from": "2026-03-01", "valid_to": "2026-02-28", ... } ] }
],
"members": [ { "id": 12, "name": "...", "email": "...", "role": "user" } ],
"todo_lists": [ { "id": 5, "name": "...", "todos": [ { "id": 91, "title": "..." } ] } ],
"counts": { "variables": 3, "timed_variables": 3, "members": 4, "todo_lists": 2, "todos": 17 }
}
}
Errors
| Code | Meaning |
|---|---|
| 400 | No variable supplied in the query string. |
| 404 | No project matches the given key/value pair. |
Note: If more than one project shares the same variable
value, the lowest-id project is returned and multiple_matches
holds the total count so callers can disambiguate.
Create Project
Creates a new project.
Request Body
| Parameter | Type | Description |
|---|---|---|
| name | string | required Project name |
| description | string | optional Project description |
$response = $client->post('/api/v1/projects', [
'json' => [
'name' => 'New client website',
'description' => 'Corporate website redesign'
]
]);
Update Project
Updates an existing project.
Delete Project
Deletes a project and all its contents. This action is irreversible!
delete.Task Lists (Todolists)
List in Project
Returns all task lists in the given project.
Create List
Creates a new task list in the project.
Request Body
| Parameter | Type | Description |
|---|---|---|
| name | string | required List name |
Tasks (Todos)
List Tasks
Returns all tasks in the given list.
Get Task
Returns detailed information about a specific task.
Create Task
Creates a new task in the list.
Request Body
| Parameter | Type | Description |
|---|---|---|
| title | string | required Task name |
| description | string | optional Task description |
| due_date | date | optional Due date (YYYY-MM-DD) |
| assigned_to | integer | optional Assigned user ID |
// 1. Get todolist ID from project
$lists = $client->get('/api/v1/projects/1/todolists');
$todolistId = $lists['data'][0]['id'];
// 2. Create task in this todolist
$response = $client->post("/api/v1/todolists/{$todolistId}/todos", [
'json' => [
'title' => 'Implement new feature',
'description' => 'Detailed task description...',
'due_date' => '2024-02-01',
'assigned_to' => 5
]
]);
$newTodo = json_decode($response->getBody(), true);
echo "Created task with ID: " . $newTodo['todo']['id'];
Update Task
Updates an existing task. You can update only the fields you want to change.
Delete Task
Deletes a task. Requires scope delete.
Add Comment
Adds a new comment to a task. Notifies subscribed users.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Task ID |
Request Body
| Parameter | Type | Description |
|---|---|---|
| content | string | required Comment text (supports HTML) |
| notify | boolean | optional Send notification to subscribers (default: true) |
curl -X POST "/api/v1/todos/123/comments" \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"content": "This is my comment on the task."}'
Update Comment
Updates an existing comment. Only the comment author can edit their comments.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Comment ID |
Request Body
| Parameter | Type | Description |
|---|---|---|
| content | string | required Updated comment text |
Delete Comment
Deletes a comment. Only the comment author or project admin can delete comments.
delete. This action is irreversible.Users
List Users
Returns a list of all users in the system that the authenticated user has access to see.
Response
Returns an array of user objects, each containing:
id- User IDname- Full nameemail- Email addressavatar- Avatar URL (if set)role- User role (user, admin, superadmin)is_active- Whether the user is active
curl -X GET "/api/v1/users" \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Accept: application/json"
Project Members
List Members
Returns a list of all members who have access to the specified project.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
Response
Returns an array of member objects with user details and their access role in the project.
Add Member
Adds a user to the project, granting them access.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
Request Body
| Parameter | Type | Description |
|---|---|---|
| user_id | integer | required ID of the user to add |
| role | string | optional Member role (default: member) |
Remove Member
Removes a user from the project, revoking their access.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
| userId | integer | required User ID to remove |
delete. The user will lose access to all project content.Knowledge Base
List Articles
Returns all knowledge base articles in the specified project.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
Response
Returns an array of article objects, each containing:
id- Article IDtitle- Article titlecontent- Article content (HTML)created_at- Creation timestampupdated_at- Last update timestampauthor- Author informationtags- Array of associated tags
Create Article
Creates a new knowledge base article in the project.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
Request Body
| Parameter | Type | Description |
|---|---|---|
| title | string | required Article title |
| content | string | required Article content (supports HTML) |
| tags | array | optional Array of tag IDs to associate |
curl -X POST "/api/v1/projects/1/knowledge" \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"title": "Getting Started Guide", "content": "<p>Welcome to our knowledge base...</p>"}'
Update Article
Updates an existing knowledge base article.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
| postId | integer | required Article ID |
Request Body
| Parameter | Type | Description |
|---|---|---|
| title | string | optional Updated title |
| content | string | optional Updated content |
Delete Article
Deletes a knowledge base article.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
| postId | integer | required Article ID to delete |
delete. This action is irreversible and will also delete all associated comments.Project Docs
List Docs
Returns documents in the selected project.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required Project ID |
Search Docs
Fulltext search in document title/content/description within one project.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| q | string | required Search term (min 2 chars) |
| limit | integer | optional Max results (default 20, max 100) |
| include_content | boolean | optional Include full HTML content in response |
Get Doc Content by Name
Returns HTML content of matching document title. If no matching document exists, returns false.
Compatibility Alias
Also available as: /project/{id}/docs/content?name=changelog
Create Doc
Creates a document in the project.
Request Body
| Parameter | Type | Description |
|---|---|---|
| title | string | optional Document title (default: Untitled Document) |
| content | string | optional HTML content |
Update Doc
Updates an existing document (title/content/flags).
Delete Doc
Deletes (moves to trash) a document.
delete.Fulltext Search
General Fulltext (Algolia)
Global permission-aware fulltext search over projects, task lists, todos and comments (Algolia-backed).
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| q | string | required Search term (min 2 chars) |
| limit | integer | optional Max hits (default 10, max 50) |
Reports
Work Report (odpracovaný čas)
Vrací odpracovaný čas v projektu za zvolené období. Data lze agregovat per úkol, per uživatele, nebo vrátit jako jednotlivé záznamy. Podporuje filtry (fakturovatelnost, uživatel, seznam úkolů) a CSV export.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | required ID projektu |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| year | integer | optional Rok (default: aktuální). Použije se s month. |
| month | integer | optional Měsíc 1–12 (default: aktuální). |
| from | date | optional Počáteční datum YYYY-MM-DD. Pokud je vyplněno, přepíše year/month. |
| to | date | optional Koncové datum YYYY-MM-DD. |
| detail | string | optional Úroveň detailu: todo (default – per úkol × uživatel), user (per uživatel), entries (jednotlivé záznamy času). |
| billable_only | boolean | optional 1 = vrátit jen fakturovatelné záznamy (default 0). |
| user_id | integer | optional Filtr na konkrétního uživatele. |
| todolist_id | integer | optional Filtr na konkrétní seznam úkolů. |
| include_users | boolean | optional Pro detail=todo: agregovat na úroveň úkolu a v každé položce vrátit pole users[] s rozpadem. |
| include_summary | boolean | optional Přidá summary_by_user[] s celkovými součty per uživatel. |
| format | string | optional json (default) nebo csv (UTF-8 s BOM, středník jako oddělovač). |
Response (JSON)
Pole items má strukturu podle parametru detail:
detail=todo:{ todo_id, todo_title, todolist_id, todolist_name, user_id, user_name, entries_count, seconds, hours, formatted }detail=todo&include_users=1:{ todo_id, todo_title, todolist_id, todolist_name, seconds, hours, formatted, entries_count, users: [...] }detail=user:{ user_id, user_name, user_email, entries_count, seconds, hours, formatted }detail=entries:{ id, started_at, ended_at, seconds, hours, formatted, billable, source, todo_id, todo_title, todolist_id, todolist_name, user_id, user_name }
curl -H "X-API-Key: YOUR_KEY" \
"/api/v1/projects/42/work-report?year=2026&month=1&detail=todo&include_users=1&billable_only=1"
{
"project": { "id": 42, "name": "Web redesign" },
"period": { "from": "2026-01-01", "to": "2026-01-31" },
"filters": { "detail": "todo", "billable_only": true, "user_id": null, "todolist_id": null },
"total_seconds": 86400,
"total_hours": 24,
"total_formatted": "24:00",
"count": 5,
"items": [
{
"todo_id": 101,
"todo_title": "Hlavička",
"todolist_id": 9,
"todolist_name": "Design",
"seconds": 18000,
"hours": 5,
"formatted": "5:00",
"entries_count": 4,
"users": [
{ "user_id": 7, "user_name": "Petr", "seconds": 12600, "hours": 3.5, "formatted": "3:30" },
{ "user_id": 9, "user_name": "Anna", "seconds": 5400, "hours": 1.5, "formatted": "1:30" }
]
}
]
}
Intake API
Jediný endpoint pro příjem klientských zakázek z venku (web formulář, e-mailový hook, externí systém). Podle IČO automaticky najde existující projekt nebo založí nový z dat z ARES, nastaví fakturaci, přiřadí výchozí tým a vytvoří úkol v novém todo-listu, který klient nevidí.
Co endpoint dělá
- Hledá projekt podle
client_ico(volitelně zúžené podletag= jméno štítku). - Pokud neexistuje, založí nový projekt: stáhne data z ARES, nastaví Time&Material billing
a přiřadí výchozí uživatele z
global_settings. - Vždy vytvoří nový todo-list s
client_visible=0(vidí jen tým). - Do listu vloží úkol přiřazený výchozímu řešiteli, případně připne štítek
tag. - Pokud zadáš
external_ref, opakovaná volání vrátí stejný úkol (idempotence).
global_settings (sekce intake, spravuje se v Admin → Nastavení → záložka Intake API):
intake_default_owner_user_id (JSON pole user IDs),
intake_default_assignee_user_id (JSON pole user IDs — všichni se uloží do todo_assignees; první je primární),
intake_default_member_user_ids (JSON pole),
intake_default_hourly_rate.
- Pokud je
billing.hourly_ratezadané, musí být > 0. - Pokud je
billing.fixed_total_pricezadaná, musí být > 0. - Pokud je
billing.fixed_total_hourszadané, musí být > 0. - Při zakládání nového projektu (IČO nebylo nalezeno) musí být zadané alespoň jedno z
billing.hourly_ratenebobilling.fixed_total_price. Bez nich nelze projekt založit — endpoint vrátí400. - Pro existující projekt billing není povinný — list se založí jako vícepráce a dědí sazbu projektu.
Submit Client Request
Přijme klientský požadavek a zařadí ho jako úkol do správného projektu. Pokud projekt neexistuje, založí ho.
Request Body
| Parameter | Type | Description |
|---|---|---|
| client_ico | string | required IČO klienta (6–8 číslic, doplní se zleva nulou na 8). |
| tag | string | optional Štítek (např. service, marketing). Pokud je zadaný, zúží hledání projektu jen na ty, které mají label s tímto jménem. Při zakládání nového projektu se label automaticky vytvoří a připne k úkolu. |
| title | string | optional Název úkolu. Pokud chybí, vygeneruje se z datumu. |
| body | string | optional Plný text požadavku (uloží se do content úkolu, podporuje HTML). |
| external_ref | string | optional Unikátní identifikátor zakázky na Tvé straně (např. ID tiketu). Při opakovaném volání se stejnou hodnotou se NEzaloží duplikát, vrátí se původní výsledek. |
| billing.hourly_rate | number | optional Hodinová sazba pro T&M projekt (v CZK, kladná). Použije se pouze při zakládání nového projektu. |
| billing.fixed_total_price | number | optional Pevná cena za celý todo-list (v CZK, kladná). Pokud je zadaná, list se vytvoří v režimu fixed_price. |
| billing.fixed_total_hours | number | optional Časová dotace (odhad) v hodinách (kladná). Pokud chybí pevná cena, list je vícepráce s tímto hodinovým budgetem. Pokud je vedle pevné ceny, slouží jen jako odhad. |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| project_id | integer | ID projektu, do kterého byl úkol zařazen. |
| todo_list_id | integer | ID nově vytvořeného listu. |
| todo_id | integer | ID vytvořeného úkolu. |
| project_action | string | linked (projekt existoval) nebo created (založen nový). |
| ares_loaded | boolean | U created: jestli se podařilo načíst ARES. |
| idempotent | boolean | Pokud true, výsledek je z dřívějšího volání se stejným external_ref. |
| warnings | array | Pole nefatálních varování (např. selhal ARES, projekt vznikl s minimem dat). |
Examples
1) Minimální požadavek – jen IČO a text. Pokud projekt s IČO existuje, úkol se přidá tam; jinak vznikne nový projekt z ARES s defaultní hodinovou sazbou.
curl -X POST /api/v1/intake/request \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"client_ico": "02232413",
"title": "Klient pošle dotaz přes web",
"body": "Plný text požadavku od klienta..."
}'
2) Se štítkem (route podle typu práce) – štítek service zužuje hledání projektu (jen ty, které tento štítek mají) a na nově vytvořený úkol se připne.
curl -X POST /api/v1/intake/request \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"client_ico": "02232413",
"tag": "service",
"title": "Servisní zásah — výpadek e-shopu",
"body": "Klient hlásí, že od 14:00 nejde objednat. Prosíme prioritní řešení.",
"external_ref": "ticket-9821"
}'
3) Vícepráce s hodinovým limitem – T&M list s rozpočtem 12 hodin (vícepráce). Hodinová sazba se použije, jen když ještě nemáš pro toto IČO projekt.
curl -X POST /api/v1/intake/request \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"client_ico": "02232413",
"tag": "marketing",
"title": "Kampaň pro září — vícepráce",
"body": "Doplnění kampaně mimo paušál. Odhadem 12 hodin.",
"billing": {
"hourly_rate": 1500,
"fixed_total_hours": 12
},
"external_ref": "tiket-2026-05-001"
}'
4) Pevná cena za celý balíček – list se založí v režimu fixed_price. fixed_total_hours je tu jen jako odhad pro reporting.
curl -X POST /api/v1/intake/request \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"client_ico": "02232413",
"tag": "service",
"title": "Rebrand — fixní zakázka",
"body": "Smluveno: kompletní rebrand za 45000 Kč. Odhad 30 hodin.",
"billing": {
"fixed_total_price": 45000,
"fixed_total_hours": 30
},
"external_ref": "smlouva-2026-031"
}'
5) PHP příklad – stejné jako 4) s Guzzle.
$response = $client->post('/api/v1/intake/request', [
'json' => [
'client_ico' => '02232413',
'tag' => 'service',
'title' => 'Rebrand — fixní zakázka',
'body' => 'Smluveno: kompletní rebrand.',
'billing' => [
'fixed_total_price' => 45000,
'fixed_total_hours' => 30,
],
'external_ref' => 'smlouva-2026-031',
],
]);
$data = json_decode($response->getBody(), true);
// $data['project_id'], $data['todo_id'], $data['project_action']
Příklad odpovědi — projekt založen (ARES)
{
"status": "ok",
"project_id": 66,
"todo_list_id": 9914,
"todo_id": 99079,
"project_action": "created",
"ares_loaded": true,
"warnings": []
}
Příklad odpovědi — idempotentní (stejný external_ref)
{
"status": "ok",
"idempotent": true,
"project_id": 66,
"todo_list_id": 9913,
"todo_id": 99078
}
project_billing_settings nedotčena —
chcete-li jinou sazbu pro konkrétní zakázku, použijte fixed_total_price (samostatný list s fixací).
Chat
Send Direct Message
Pošle přímou (direct) zprávu do chatu konkrétnímu uživateli. Pokud mezi odesílatelem
a příjemcem ještě neexistuje přímá konverzace, automaticky se vytvoří. Zpráva se příjemci
doručí real-time (Pusher) a pokud je offline/idle, dostane push notifikaci (OneSignal).
Vyžaduje scope write.
Request Body
| Parameter | Type | Description |
|---|---|---|
| login | string | required Login (e-mail) příjemce zprávy, např. jan.novak@firma.cz. |
| sender | string | optional Kdo zprávu odesílá: me = uživatel, kterému patří API klíč (default), nebo autobot = systémový Autobot účet (pro automatizované/systémové zprávy). Pokud Autobot v instalaci ještě neexistuje, založí se automaticky při prvním použití. |
| text | string | required Text zprávy (alias: message). |
Response (201 Created)
| Field | Type | Description |
|---|---|---|
| data.message_id | integer | ID vytvořené zprávy. |
| data.conversation_id | integer | ID přímé konverzace (existující nebo nově vytvořené). |
| data.sender | string | me nebo autobot. |
| data.sender_id | integer | ID uživatele-odesílatele. |
| data.recipient_id | integer | ID příjemce. |
| data.created_at | string | Čas vytvoření zprávy. |
Error Codes
| Code | Description |
|---|---|
400 | Chybí login/text, neplatný sender, odesílatel = příjemce, nebo API klíč nemá přiřazeného uživatele (u sender=me). |
401 | Chybějící nebo neplatný API klíč. |
403 | API klíč nemá scope write. |
404 | Příjemce s daným loginem neexistuje. (Autobot účet se při prvním použití založí automaticky.) |
429 | Překročen rate limit — počkejte podle hlavičky X-RateLimit-Reset. |
1) Zpráva jménem vlastníka API klíče
curl -X POST /api/v1/chat/messages \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"login": "jan.novak@firma.cz",
"sender": "me",
"text": "Ahoj, mrkni prosím na ten nový úkol."
}'
2) Systémová zpráva od Autobota — pro automatizace (cron, n8n, monitoring), zpráva přijde od systémového účtu Autobot.
curl -X POST /api/v1/chat/messages \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"login": "jan.novak@firma.cz",
"sender": "autobot",
"text": "Deploy projektu XYZ proběhl úspěšně ✅"
}'
Příklad odpovědi
{
"status": "ok",
"data": {
"message_id": 48211,
"conversation_id": 392,
"sender": "autobot",
"sender_id": 7,
"recipient_id": 15,
"created_at": "2026-06-10 14:32:08"
}
}
Comments
List Comments
Returns all comments for a specific task, ordered by creation date (newest first).
Path Parameters
Response
Returns an array of comment objects, each containing:
id- Comment IDcontent- Comment text (may contain HTML)created_at- Creation timestampuser- Author information (id, name, avatar)attachments- Array of attached files