Aller au contenu

Conventions

The same rules apply to every endpoint. The reference gives the details of each one.

Requests and responses

  • Bodies are JSON (Content-Type: application/json), in UTF-8. Field names are in camelCase.
  • Identifiers are strings. Treat them as opaque.
  • Instants are UTC in ISO 8601 (2026-10-16T08:12:44Z); calendar dates have no time (2026-11-15).
  • Enumeration values are lowercase snake_case: open, geo_map, read_write.
  • Fields without a value are left out of responses rather than sent as null.
  • A field the API does not know is an error, not ignored: a typo such as adress gets 400.
Request Success
GET 200 with the resource or the page
POST that creates 201, the new resource, and its URL in Location
PATCH, PUT, actions such as POST /v1/tasks/{taskId}/resolve 200 with the resource as it now is
DELETE 204 with no body

Pagination

Lists return one page at a time:

{
  "data": [ ... ],
  "pagination": { "limit": 50, "hasMore": true, "nextCursor": "eyJvIjo1MH0.k3P..." }
}
  • limit sets the page size: 50 by default, 200 at most.
  • To get the next page, send the same request with cursor set to nextCursor. The last page has hasMore: false and no nextCursor.
  • A cursor is bound to the filters of the request that produced it. Changing a filter while paging gets invalid_cursor: start again without a cursor.
def all_tasks(api, project_id, **filters):
    params = {"limit": 200, **filters}
    while True:
        page = api.get(f"https://api.touch2build.com/v1/projects/{project_id}/tasks", params=params).json()
        yield from page["data"]
        if not page["pagination"]["hasMore"]:
            return
        params["cursor"] = page["pagination"]["nextCursor"]

Filters and sort

Filters are query parameters. Repeat a parameter to accept several values (status=open&status=resolved). sort takes one field, with - in front for descending order: sort=-due_date. Each list documents its filters and sort fields.

Partial updates

PATCH follows JSON Merge Patch (RFC 7396), sent as application/merge-patch+json (or application/json):

  • a field you leave out is not changed;
  • a field set to null is cleared;
  • any other value replaces the current one.
curl -X PATCH https://api.touch2build.com/v1/tasks/$TASK_ID \
  -H "Authorization: Bearer $T2B_API_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{"dueDate": "2026-12-01", "description": null}'

A status change is not a PATCH: use the actions resolve, close, reopen and forward.

Retrying creations safely

When a request times out, you cannot tell whether the server created the resource. Creating a task (POST /v1/projects/{projectId}/tasks) and a comment (POST /v1/tasks/{taskId}/comments) accept an Idempotency-Key header: a unique string of 8 to 64 letters, digits or ._:-, such as a UUID. Retrying with the same key returns the resource the first request created instead of a duplicate.

Generate the key once per resource you want to create, and keep it for the retries.

Files

  • Downloads are sub-resources such as /content, /image or /thumbnail, and answer with the file itself (image/jpeg, application/pdf...).
  • Photos and other files are uploaded as the raw request body, with their Content-Type. Plans are the exception: multipart/form-data with the PDF and its metadata.
  • A request body is limited to 50 MB.
  • A plan is processed after its upload. Until it is ready, its image and file answer plan_processing: retry a few seconds later.

Rate limits

Requests are limited per API key (or per token). Over the limit, the API answers 429 with the code rate_limited and a Retry-After header giving the seconds to wait. Wait that long before retrying, and spread bulk jobs over time instead of sending requests in parallel.

Request identifiers

Every response carries an X-Request-Id header, also found in error bodies as requestId. Quote it when you contact support. You can send your own X-Request-Id (1 to 64 letters, digits or ._-) to follow a request through your logs and ours.