Обзор
API построено на GraphQL: все операции идут одним POST-запросом на адрес выше. Тело запроса — JSON с полями query и (необязательно) variables.
Ключевые свойства, которые стоит знать до начала работы:
Ключ привязан к одному пространству
Пространство выбирается в момент выпуска ключа и не меняется. Всё, что видит и меняет ключ, находится внутри этого пространства — обратиться к соседнему нельзя даже зная идентификаторы объектов.
Ключ действует от имени человека
Это тот, кто его выпустил. Все проверки доступа к проектам работают ровно так же, как если бы этот пользователь зашёл в интерфейс.
Изменения видны мгновенно
Всё, что API создаёт и меняет, рассылается в открытые вкладки Таски по вебсокету — пользователю не нужно перезагружать страницу.
Правила приложения соблюдаются
Лимиты тарифа, платные функции, права ролей, автоматическая смена исполнителя при переходе между статусами, правила повторения задач — всё работает так же, как в интерфейсе. API не является обходным путём.
Ключи доступа
Ключ выпускается в интерфейсе Таски. Где именно — зависит от того, к какому пространству нужен доступ:
| Пространство | Где выпустить | Кто может |
|---|---|---|
| Личное | Профиль → вкладка API | Владелец аккаунта |
| Командное | Настройки пространства → вкладка API | Владелец и администраторы пространства |
Ключ выглядит так:
taska_k3f9a1c7b204_x8Qm2LpZv...
Он состоит из трёх частей: префикса taska, публичного идентификатора ключа и секрета. Публичный идентификатор виден в интерфейсе и в журнале обращений — по нему удобно понять, какая именно интеграция сделала запрос.
Авторизация
Ключ передаётся в заголовке Authorization:
Authorization: Bearer taska_k3f9a1c7b204_x8Qm2LpZv...
Если ваш прокси или платформа перехватывает Authorization, используйте запасной заголовок — он равнозначен:
X-Taska-Api-Key: taska_k3f9a1c7b204_x8Qm2LpZv...
Проверить ключ можно запросом viewer — он не требует никаких прав и возвращает контекст, в котором работает ключ:
query {
viewer {
tokenName
tokenPrefix
scopes
spaceId
spaceTitle
actingUserEmail
expiresAt
rateLimitPerMinute
}
}
Когда ключ перестаёт работать
Запрос вернёт 401 с кодом unauthenticated, если:
- ключ отозван или удалён;
- истёк заданный при выпуске срок действия;
- запрос пришёл с адреса вне белого списка IP (если список задан);
- пользователь, от имени которого выпущен ключ, покинул пространство или был из него исключён. Это проверяется на каждом запросе: отключение сотрудника автоматически закрывает его интеграции, без ручной чистки ключей.
Быстрый старт
curl -s http://taska.be/api/v1/graphql \
-H "Authorization: Bearer $TASKA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ projects { totalCount items { id title type } } }"}'
async function taska(query, variables = {}) {
const response = await fetch('http://taska.be/api/v1/graphql', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.TASKA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ query, variables }),
});
const payload = await response.json();
// GraphQL отвечает 200 даже на прикладные ошибки — проверяем тело ответа,
// а не только HTTP-статус.
if (payload.errors?.length) {
const first = payload.errors[0];
throw new Error(`[${first.extensions?.code}] ${first.message}`);
}
return payload.data;
}
const { createTask } = await taska(`
mutation NewTask($statusId: ID!, $title: String!) {
createTask(statusId: $statusId, title: $title) {
task { id title status { title } }
}
}
`, { statusId: '42', title: 'Задача из внешнего сервиса' });
import os
import requests
ENDPOINT = "http://taska.be/api/v1/graphql"
SESSION = requests.Session()
SESSION.headers["Authorization"] = f"Bearer {os.environ['TASKA_API_KEY']}"
def taska(query: str, **variables):
response = SESSION.post(ENDPOINT, json={"query": query, "variables": variables})
payload = response.json()
if payload.get("errors"):
error = payload["errors"][0]
code = (error.get("extensions") or {}).get("code")
raise RuntimeError(f"[{code}] {error['message']}")
return payload["data"]
data = taska(
"""
query OpenTasks($limit: Int) {
tasks(filter: {isCompleted: false}, orderBy: DEADLINE, orderDirection: ASC, limit: $limit) {
totalCount
items { id title deadline performer { fullName } }
}
}
""",
limit=50,
)
Права доступа
При выпуске ключа выбирается набор прав. Каждая операция требует конкретного права; при его отсутствии запрос вернёт ошибку с кодом forbidden и подсказкой, чего именно не хватает.
Права на чтение и на запись независимы: tasks:write не включает tasks:read. Это сделано специально — так можно выдать ключ, который только создаёт задачи из внешней системы, но не может выгрузить содержимое пространства.
| Право | Что открывает |
|---|---|
| space:read | Чтение данных пространства |
| members:read | Чтение списка участников пространства |
| projects:read | Чтение проектов, шаблонов, десков и коннекторов |
| projects:write | Создание, изменение, архивация и удаление проектов |
| boards:read | Чтение досок, статусов, тегов, полей задач и эпиков |
| boards:write | Изменение структуры досок, статусов, тегов, полей и эпиков |
| tasks:read | Чтение задач, чек-листов, значений полей и файлов |
| tasks:write | Создание и изменение задач, чек-листов и значений полей |
| knowledge:read | Чтение хранилищ, папок и страниц базы знаний |
| knowledge:write | Создание и изменение хранилищ, папок и страниц |
Ошибки
Ошибки приходят в стандартном для GraphQL виде. Ориентируйтесь на extensions.code — это стабильный машиночитаемый идентификатор. Текст message предназначен человеку и может меняться.
{
"data": null,
"errors": [
{
"message": "Ключу не хватает права «tasks:write». Выданные права: tasks:read.",
"extensions": {
"code": "forbidden",
"required_scope": "tasks:write",
"granted_scopes": ["tasks:read"]
}
}
]
}
| Код | HTTP | Что означает и что делать |
|---|---|---|
| unauthenticated | 401 | Ключ не передан, неверен, отозван, истёк, либо у его пользователя больше нет доступа к пространству. Повтор не поможет — нужен новый ключ. |
| forbidden | 200 / 403 | Ключу не хватает права, либо у пользователя нет полномочий на операцию (например, гость не создаёт проекты). Добавьте право в настройках ключа. |
| not_found | 200 | Объекта нет либо он вне пространства ключа. Эти два случая намеренно неразличимы. |
| invalid_input | 200 / 400 | Аргументы не прошли валидацию. Сообщение содержит список допустимых значений. |
| conflict | 200 | Операция противоречит текущему состоянию: правка архивного проекта, удаление непустой колонки, дубль связи. |
| quota_exceeded | 200 | Исчерпан лимит тарифа (проекты, доски, статусы). В extensions есть limit, current, max. |
| feature_not_allowed | 200 | Функция недоступна на текущем тарифе пространства — например, платный блок статьи. |
| rate_limited | 429 | Превышена частота запросов. Заголовок Retry-After подскажет, через сколько секунд повторить. |
| query_too_complex | 400 | Слишком глубокая вложенность запроса. Разбейте на несколько. |
| internal_error | 200 / 500 | Ошибка на нашей стороне. Имеет смысл повторить с экспоненциальной задержкой; если повторяется — напишите в поддержку и укажите время запроса. |
errors в теле ответа — как в примерах выше.
Пагинация
Все списки устроены одинаково: аргументы limit и offset, ответ одной формы.
query Page($offset: Int!) {
tasks(limit: 100, offset: $offset) {
items { id title }
totalCount # всего под фильтр
hasMore # есть ли следующая страница
limit # фактически применённый размер страницы
offset
}
}
Значение limit по умолчанию — 50, максимум — 200. Запрос большего значения не даст ошибки: он будет тихо уменьшен до максимума, а фактический размер вернётся в поле limit. Проверяйте его, если нарезаете выгрузку по страницам.
Полная выгрузка выглядит так:
async function fetchAll(query, variables = {}) {
const all = [];
let offset = 0;
for (;;) {
const page = (await taska(query, { ...variables, offset })).tasks;
all.push(...page.items);
if (!page.hasMore) break;
// Шагаем на фактический limit, а не на запрошенный — сервер мог его урезать.
offset += page.limit;
}
return all;
}
Лимиты и ограничения
Частота запросов
Лимит задаётся для каждого ключа отдельно (по умолчанию 120 запросов в минуту). Мутация расходует бюджет как два обычных запроса — она пишет в базу и рассылает уведомления.
Каждый ответ содержит текущее состояние счётчика:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 96
X-RateLimit-Reset: 34
При превышении вернётся 429 и заголовок Retry-After. Правильная реакция — подождать указанное число секунд, а не повторять сразу.
Сложность запроса
Максимальная вложенность — 12 уровней. Связи в схеме цикличны (проект → доски → статусы → доска → проект), и без ограничения одним запросом можно было бы вытянуть всю базу.
Лимиты тарифа
Количество проектов, досок и статусов ограничено тарифом пространства ровно так же, как в интерфейсе. При исчерпании вернётся quota_exceeded с деталями.
Безопасность
Ключ — это пароль
Храните его в переменных окружения или секрет-хранилище. Не коммитьте в репозиторий, не кладите во фронтенд-код и не передавайте в query-параметрах URL — они попадают в логи прокси.
Отдельный ключ на интеграцию
Тогда утечка или сбой одного сервиса закрывается ротацией одного ключа, а по журналу видно, кто что делал.
Белый список IP
Если интеграция ходит с фиксированных адресов, укажите их при выпуске ключа — украденный ключ станет бесполезен вне вашей сети. Поддерживаются отдельные адреса и подсети (203.0.113.0/24).
Срок жизни
Для временных задач (миграция, разовая выгрузка) задавайте срок в днях — ключ отключится сам.
Журнал обращений
Мутации и любые отказы записываются. Историю последних обращений видно в карточке ключа.
Модель данных
Иерархия сущностей внутри пространства:
Space (пространство — привязано к ключу)
├── Member участники
└── Project проект
├── Board канбан-доска
│ └── Status колонка доски
│ └── Task задача
│ ├── Todo пункт чек-листа
│ ├── Tag тег
│ └── FieldValue значение пользовательского поля
├── TaskField описание поля задач проекта
├── Epic эпик (строка диаграммы Гантта)
│ └── EpicDependency зависимость между эпиками
└── Storage хранилище базы знаний
├── Folder папка (может быть вложенной)
└── Page страница
Полезные детали:
- Задача может существовать вне доски — во «входящих» пространства. Такие задачи ищутся фильтром
{ inInbox: true }. - Тип проекта (
simple,complex,storage,roadmap) определяет, что создаётся автоматически. Уsimpleсразу появляется доска с набором статусов, уstorage— хранилище базы знаний. - Поля задач бывают системными (у них заполнен
builtinKey: описание, исполнитель, статус, приоритет, дедлайн, теги, эпик) и пользовательскими. Системные нельзя удалять и переименовывать — только скрывать и переставлять. - Допустимые значения всех перечислимых полей (приоритеты, цвета, иконки, типы) отдаёт запрос
metadata. Запросите его один раз при настройке интеграции.
Блочный редактор
Страница базы знаний бывает трёх типов, и тип задаётся один раз, при создании (pageType): wysiwyg — форматированный HTML, markdown — обычный markdown, blocks — блочный редактор. Последний и есть основной формат статей: из него собираются таблицы, диаграммы, колонки, чек-листы, карточки задач, живые выборки задач и всё остальное, чего в тексте выразить нельзя.
Содержимое блочной страницы лежит в том же поле content, но это строка с JSON-документом. API принимает её как есть и не разбирает: формат — контракт редактора, и вся ответственность за него на том, кто пишет. Поэтому ниже он описан целиком.
Документ
{
"version": 1,
"blocks": [
{ "id": "blk_Qa7vMx0Lp2", "type": "heading", "data": { "level": 1, "spans": [ { "text": "Регламент" } ] } },
{ "id": "blk_8fKd1sZbNe", "type": "paragraph", "data": { "spans": [ { "text": "Как обрабатывать заявки." } ] } }
]
}
id— стабильный идентификатор блока,blk_+ 10 символов base62. По нему адресуются правки, якоря оглавления и ссылка диаграммы на таблицу, поэтому уникальность обязательна: копируя блок, выдайте копии новый id.type— тип блока из таблицы ниже. Неизвестный тип редактор превращает в пустой абзац: содержимое такого блока теряется молча.data— поля блока. Набор зависит от типа; отсутствующие поля дополняются значениями по умолчанию при чтении.version— версия формата документа. Сейчас всегда1.
Текст: сегменты и метки
Форматированный текст — это не HTML, а массив сегментов. Каждый сегмент — кусок текста с набором меток:
"spans": [
{ "text": "Обычный текст, " },
{ "text": "жирный", "marks": [ { "type": "bold" } ] },
{ "text": " и ссылка", "marks": [ { "type": "link", "href": "https://taska.be" } ] }
]
| type | Поля | Что означает |
|---|---|---|
| bold, italic, underline, strike, code | — | Начертание. Комбинируются свободно. |
| color, bgColor | value | Ключ палитры: emerald, green, lime, yellow, amber, orange, red, scarlet, pink, fuchsia, purple, violet, indigo, blue, sky, cyan, teal, slate, gray, zinc, silver, stone. Не CSS-цвет: оттенок берётся из темы. |
| link | href | Внешняя ссылка. Разрешены схемы http, https, mailto, tel, ftp и относительные пути; всё остальное (в первую очередь javascript:) отбрасывается при чтении. |
| pageLink | pageId, snapshot | Ссылка на другую страницу базы знаний. По этим меткам сервер пересобирает граф связей страницы, поэтому ссылаться можно только на страницы того же хранилища. snapshot — название на момент вставки, оно останется в тексте, даже если страницу переименуют. |
| mention | userId, snapshot | Упоминание участника. Идентификаторы берите в запросе members. Упомянутый получит уведомление — ровно один раз на каждое появление в статье, как и при упоминании из интерфейса. Упоминание того, кто не состоит в пространстве, игнорируется. |
| comment | threadId | Привязка фрагмента к ветке обсуждения. Сам текст комментариев живёт на сервере, в документе только метка. |
link, pageLink и mention помечают цельную сущность, а не оформление: они относятся ко всему сегменту целиком. Остальные метки можно накладывать на любые куски текста и комбинировать между собой.
Кроме плоского spans у некоторых блоков есть отдельные текстовые поля — caption, title, body, summary. Все они устроены одинаково: объект { "spans": [ … ] }.
Типы блоков
| type | Блок | Поля data |
|---|---|---|
| paragraph | Абзац | spans |
| heading | Заголовок | level (1–6, по умолчанию 2), spans |
| quote | Цитата | spans, caption |
| code | Код | code (плоский текст, без меток), language (id языка highlight.js или auto), filename, wrap, lineNumbers |
| divider | Разделитель | style: solid / stars (по умолчанию) / dashed |
| link | Ссылка-карточка | href, label |
| image | Картинка | slug, alt, caption, width, height, align (left / center / right) |
| embed | Встройка | url, height (160–1200), caption |
| files | Файлы | view (grid / list), folders[], items[], order[] |
| warning | Выноска | kind (info / warn / danger), title, body |
| list | Список | style (bullet / numbered), items[]: id, spans, level |
| checklist | Чек-лист | items[]: id, checked, spans, level |
| table | Таблица | widths[], rows[]: id, cells[]: id, spans, align, valign; headerRow, headerCol |
| chart | Диаграмма | sourceId (id блока-таблицы), kind (bar / horizontalBar / line / area / pie / doughnut / radar), labelCol, valueCols[], height (160–900), legend, stacked, caption |
| toc | Оглавление | maxLevel (2–6), mode (full / left / center / right), numbered |
| layout | Колонки | columns (2 или 3), cells[][] — по списку блоков на колонку, ratios[] |
| toggle | Сворачиваемая секция | summary, open, cells[][] — ровно один вложенный список блоков |
| task | Карточка задачи | taskId |
| taskList | Выборка задач | boardId, statusIds[], performerIds[], tagIds[], epicId, state (open / closed / all), limit (1–100), title |
| synced | Переиспользуемый фрагмент | fragmentId |
Что важно знать про отдельные блоки:
- Диаграмма своих цифр не хранит. Она ссылается полем
sourceIdна блок-таблицу в этой же статье, аlabelColиvalueCols— индексы её колонок (с нуля). Сначала вставьте таблицу, потом диаграмму с еёid. - Оглавление вычисляется на лету из заголовков статьи — записывать в него пункты не нужно и некуда.
- Карточка и выборка задач — живые. В документе лежат только идентификаторы и фильтры; названия, статусы и исполнители подтягиваются при открытии статьи.
- Картинки и файлы должны быть уже загружены. В документе хранится
slugфайла, а загрузка идёт не через GraphQL. Возьмитеslugуже загруженного файла запросомfiles. - Встраиваются только известные сервисы — Figma, Miro, YouTube, RUTUBE, VK Видео, Vimeo, Loom, документы Google и Яндекса, CodePen. Прочие ссылки блок покажет карточкой: почти все сайты запрещают встраивать себя во фрейм.
- Код — плоский текст. Подсветка вычисляется при рендере, поэтому меток внутри
codeнет и быть не может. - Переиспользуемый фрагмент — отдельная сущность хранилища: один и тот же текст, показанный в нескольких статьях. В публичном API его нет, в документе хранится только
fragmentId.
Вложенность
Вкладывать блоки умеют ровно два типа — layout (2–3 колонки) и toggle (одна сворачиваемая секция). Оба хранят вложенное в data.cells — массиве списков блоков того же формата, что и blocks документа:
{
"id": "blk_Lc93pQmT1x",
"type": "layout",
"data": {
"columns": 2,
"ratios": [1, 1],
"cells": [
[ { "id": "blk_a1", "type": "paragraph", "data": { "spans": [ { "text": "Слева" } ] } } ],
[ { "id": "blk_b2", "type": "paragraph", "data": { "spans": [ { "text": "Справа" } ] } } ]
]
}
}
- Колонки нельзя вкладывать в колонки, а в сворачиваемую секцию — ни колонки, ни другую секцию.
- Длина
cellsравнаcolumns; уtoggleячейка всегда одна. - Пустая ячейка при чтении получает пустой абзац — иначе в неё некуда поставить курсор.
- Вложенность пунктов списка — это не дерево, а поле
levelу пункта (0–4).
Ограничения
| Что | Предел | Что будет при превышении |
|---|---|---|
| Размер документа | 1 МБ | Редактор откажется сохранять статью — разбейте её на несколько. |
| Таблица | 10 колонок, 100 строк | Лишнее отбрасывается при чтении. |
| Вложенность пунктов списка | 4 | Уровень обрезается. |
| Высота встройки | 160–1200 | Значение зажимается в диапазон. |
| Высота диаграммы | 160–900 | Значение зажимается в диапазон. |
chart, files, task и taskList — платная функция. Добавить новый такой блок на бесплатном тарифе нельзя: запись вернёт ошибку feature_not_allowed. Уже стоящие в статье блоки при этом никуда не деваются — их видно, а текст вокруг них правится как обычно, поэтому сохранять статью с ними API не мешает. Решает тариф проекта, которому принадлежит хранилище статьи, а не подписка владельца ключа.
Как писать блочную статью
Содержимое передаётся строкой, поэтому документ удобнее собрать объектом и сериализовать в переменную запроса — экранировать JSON внутри JSON руками не нужно:
mutation NewArticle($storageId: ID!, $content: String!) {
createPage(
storageId: $storageId
title: "Регламент обработки заявок"
pageType: "blocks"
content: $content
) {
page { id title pageType }
}
}
{
"storageId": "4",
"content": "{\"version\":1,\"blocks\":[ … ]}"
}
Небольшая статья целиком — заголовок, текст, таблица и диаграмма по ней:
{
"version": 1,
"blocks": [
{ "id": "blk_h1", "type": "heading", "data": { "level": 1, "spans": [ { "text": "Выручка за квартал" } ] } },
{ "id": "blk_p1", "type": "paragraph", "data": { "spans": [
{ "text": "Подробности — в " },
{ "text": "соседней статье", "marks": [ { "type": "pageLink", "pageId": "31", "snapshot": "Методика расчёта" } ] },
{ "text": ". Ответственный: " },
{ "text": "Иван", "marks": [ { "type": "mention", "userId": "7", "snapshot": "Иван Петров" } ] }
] } },
{ "id": "blk_t1", "type": "table", "data": {
"widths": [1, 1],
"headerRow": true,
"headerCol": false,
"rows": [
{ "id": "row_1", "cells": [
{ "id": "c11", "spans": [ { "text": "Месяц" } ], "align": "left", "valign": "top" },
{ "id": "c12", "spans": [ { "text": "Выручка" } ], "align": "right", "valign": "top" } ] },
{ "id": "row_2", "cells": [
{ "id": "c21", "spans": [ { "text": "Январь" } ], "align": "left", "valign": "top" },
{ "id": "c22", "spans": [ { "text": "120" } ], "align": "right", "valign": "top" } ] }
] } },
{ "id": "blk_c1", "type": "chart", "data": {
"sourceId": "blk_t1", "kind": "bar", "labelCol": 0, "valueCols": [1],
"height": 320, "legend": true, "stacked": false,
"caption": { "spans": [ { "text": "Помесячно, тыс. ₽" } ] } } }
]
}
Как править существующую статью
Гранулярных операций над блоками в публичном API нет: правка — это «прочитать content, изменить нужный блок, записать документ целиком». Блок ищите по id, а не по позиции: позиции меняются при каждом редактировании в интерфейсе.
query Read($id: ID!) {
page(id: $id) { id pageType content updatedAt }
}
mutation Write($id: ID!, $content: String!) {
updatePage(id: $id, content: $content) { page { id updatedAt } }
}
content непосредственно перед записью и не держите документ в памяти между прогонами.
Тип страницы при этом менять нельзя: markdown-строка в блочной странице покажется одним абзацем, а JSON-документ в markdown-странице — простынёй фигурных скобок. Если формат не тот, заведите новую страницу.
pageLink-метки и ссылки вида /storages/<id>/pages/<id> превращаются в список связанных страниц, а mention-метки — в уведомления упомянутым и в строки ленты проекта.
Рецепты
Создать задачу в конкретной колонке
Достаточно указать statusId — доска и проект определяются автоматически:
mutation {
createTask(
statusId: "42"
title: "Проверить заявку №1043"
description: "Пришла из внешней системы"
priority: "second"
deadline: "2026-08-15T12:00:00+03:00"
performerId: "7"
tagIds: ["3", "5"]
) {
task { id title status { title } performer { fullName } }
}
}
Создать задачу во «входящих» пространства
mutation {
createTask(title: "Разобрать позже") {
task { id }
}
}
Найти нужную колонку по названию
Идентификаторы статусов удобно один раз получить и закешировать у себя — они не меняются:
query {
boards(projectId: "12") {
items {
id
title
statuses { id title icon }
}
}
}
Передвинуть задачу и закрыть её
mutation Advance($id: ID!, $statusId: ID!) {
moveTask(id: $id, statusId: $statusId, position: 1) {
task { id status { title } }
}
}
mutation Finish($id: ID!) {
completeTask(id: $id) {
rescheduled # true — задача повторяющаяся и перенесена на след. итерацию
task { isCompleted completedAt }
}
}
moveTask применит её — и отправит исполнителю уведомление, как при перетаскивании карточки мышью.
Очистить необязательное поле
Мутации изменения затрагивают только те поля, которые вы передали. Чтобы очистить поле, передайте null явно:
mutation {
clearDeadline: updateTask(id: "301", deadline: null) { task { deadline } }
unassign: updateTask(id: "302", performerId: null) { task { performer { id } } }
}
Записать значения пользовательских полей
query FieldsOfProject {
taskFields(projectId: "12") {
items { id title fieldType options builtinKey }
}
}
mutation {
setTaskFieldValues(
taskId: "301"
values: [
{ fieldId: "8", value: "ООО «Ромашка»" }, # text
{ fieldId: "9", value: 14990 }, # number
{ fieldId: "10", value: "opt_a1b2c3" }, # select — ОДИН id опции
{ fieldId: "11", value: ["opt_x1", "opt_y2"] }, # multiselect — список id
{ fieldId: "12", value: null } # очистить значение
]
) {
task { fieldValues { fieldId value } }
}
}
select принимает один идентификатор опции строкой, multiselect — список идентификаторов, checkbox — булево, date и datetime — строку ISO-8601. Идентификаторы опций берите из поля options запроса taskFields: они стабильны и не меняются при переименовании опции.
Пустое значение любого типа (null, пустая строка, пустой список) трактуется как «очистить поле»: значение удаляется, а не сохраняется пустым.
Задачи с дедлайном на неделю вперёд
query Upcoming($from: DateTimeScalar!, $to: DateTimeScalar!) {
tasks(
filter: { isCompleted: false, deadlineFrom: $from, deadlineTo: $to }
orderBy: DEADLINE
orderDirection: ASC
limit: 200
) {
totalCount
items {
id title deadline
project { title }
performer { fullName email }
}
}
}
Завести страницу в базе знаний
mutation {
createPage(
storageId: "4"
title: "Регламент обработки заявок"
pageType: "markdown"
content: "## Шаг 1\n\nПроверить реквизиты."
) {
page { id title pageType }
}
}
Так создаётся простая markdown-страница. Для полноценной статьи — с таблицами, диаграммами, колонками, чек-листами и карточками задач — нужен pageType: "blocks" и JSON-документ в content: формат описан в разделе Блочный редактор. Тип страницы фиксируется при создании и потом не меняется.
Инкрементальная синхронизация
Чтобы не выкачивать всё пространство при каждом прогоне, используйте фильтр updatedFrom. Он есть у задач, проектов и страниц и отдаёт всё, что изменилось после указанного момента.
query Changed($since: DateTimeScalar!, $offset: Int!) {
tasks(
filter: { updatedFrom: $since }
orderBy: UPDATED
orderDirection: ASC
limit: 200
offset: $offset
) {
items { id title updatedAt isCompleted status { id } }
hasMore
limit
}
}
Удаления через updatedFrom не приходят: удалённый объект просто перестаёт появляться в выдаче. Если для вас важны удаления, сверяйте набор идентификаторов со своей копией.
Справочник: запросы
Список собран прямо из работающей схемы, поэтому не может устареть. Полное описание типов — в SDL-схеме; её же удобно скормить генератору типизированных клиентов.
Полный список запросов с аргументами и возвращаемыми типами доступен в SDL-схеме и подгружается в интерактивном виде после загрузки страницы.
Справочник: мутации
Полный список мутаций с аргументами и возвращаемыми типами доступен в SDL-схеме и подгружается в интерактивном виде после загрузки страницы.