Introduction

Introduction

Base URL

The Tailglow API is built on REST principles. We enforce HTTPS in every request to improve data security, integrity, and privacy. The API does not support HTTP.

All requests contain the following base URL:

https://api.tailglow.io

Authentication

To authenticate with the Tailglow API, send your API key in the Authorization header using the Bearer scheme. An API key looks like tg_api_ followed by 70 characters.

Authorization: Bearer tg_api_1a2B3c4D5e6F7g8H9i0J...

Authorization: Key <your key> is also accepted, as is sending the key on its own with no scheme. Keys issued before the tg_api_ prefix begin with sk_ and keep working indefinitely.

Filtering Data

When filtering data, you’ll create a query string that will be appended to the URL of your GET request. The query string will begin with a ? and contain key-value pairs separated by &.

Be careful when using "not" filters. They can result in a lot of data being returned.

Tailglow uses a single canonical operator vocabulary across every filter surface. URL syntax is flat: ?<field>=<op>:<value>. The full list, per-type allowlists, and per-surface availability matrix live in the Filter Operators enum.

Strings

  • equals - ?type=equals:expense
  • not_equals - ?type=not_equals:expense
  • starts_with - ?type=starts_with:expense
  • not_starts_with - ?type=not_starts_with:expense
  • ends_with - ?type=ends_with:expense
  • not_ends_with - ?type=not_ends_with:expense
  • contains - ?type=contains:expense
  • not_contains - ?type=not_contains:expense
  • in - ?type=in:expense,refund
  • not_in - ?type=not_in:expense,refund

String operators compare case-insensitively, so ?type=equals:GET and ?type=equals:get include the same rows.

Numbers

  • equals - ?value=equals:100
  • not_equals - ?value=not_equals:100
  • gt (Greater Than) - ?value=gt:100
  • gte (Greater Than or Equal) - ?value=gte:100
  • lt (Less Than) - ?value=lt:100
  • lte (Less Than or Equal) - ?value=lte:100
  • between - ?value=between:100,200
  • in / not_in - ?value=in:100,200

Pagination

Most list endpoints use cursor-based pagination. The first request omits the cursor; subsequent requests echo back the next_cursor (or prev_cursor) the previous response handed you.

Request parameters

  • limit number Maximum number of items to return per page. Defaults to 25, max 200.
  • after string Opaque cursor from a previous response’s pagination.next_cursor. Returns the rows after the pivot.
  • before string Opaque cursor from a previous response’s pagination.prev_cursor. Returns the rows before the pivot. Mutually exclusive with after.
  • sort "asc" \| "desc" Sort direction. Cursors are minted under one direction; reusing them under a different sort returns a 400.

Response shape

The pagination object on every list response carries:

  • limit number: echoed from the request.
  • count number: number of rows in this page (≤ limit).
  • offset number: 0-indexed position of the first row across the full result set. Use offset + 1 for “Showing 21” labels.
  • sort "asc" \| "desc": echoed sort direction.
  • next_cursor string \| null: cursor for the next page. null means there are no more rows.
  • prev_cursor string \| null: cursor for the previous page. null means you’re on the first page.
  • total number \| null: exact row count for database-backed lists and records. null is returned only when the underlying feed cannot provide a total. For example, collection docs always return null because the underlying files aren’t enumerated.

Cursors are server-minted opaque tokens. Don’t construct them yourself; round-trip the values the API gave you.

Responses

All of our responses will follow a standard structure, regardless if it’s successful or not. Here is an example of the response object.

Successful Response Example

If you are listing data, the data key will be an array of objects and a pagination object will be present. If you are retrieving, creating, or writing a single object, the data key will be an object. If you are deleting an object, the data key will be null.

{
  "message": "Data retrieved successfully",
  "data": [],
  "status": 200,
  "error": null,
  "pagination": {
    "object": "pagination",
    "limit": 10,
    "count": 10,
    "offset": 0,
    "sort": "desc",
    "next_cursor": "eyJ2IjoxLCJyZXNvdXJjZSI6...",
    "prev_cursor": null,
    "total": 42
  },
  "endpoint": "/v1/projects"
}

Error Response Example

You’ll typically see error when request validations fail. The error key will contain an object with a code key and a properties key that will contain an array of objects with an id and message key. The id corresponds to the field that failed validation and the message will contain the error message.

{
  "message": "The request is invalid",
  "data": null,
  "status": 400,
  "error": {
    "code": "request_invalid",
    "properties": [
      {
        "id": "name",
        "message": "Name is required"
      }
    ]
  },
  "pagination": null,
  "endpoint": "/v1/projects"
}