Tether
DocsGitHub

Guides

Describing your API for the assistant

An OpenAPI document is written for developers. The assistant reads the same text to decide which operation answers a question and what to send it, and the facts it needs most are usually the ones nobody wrote down. This guide is what we learned onboarding a car-repair finder: 82 operations, no summaries, and three missing sentences that decided whether its assistant worked.

Starting from a GitHub repository

No OpenAPI file at hand? In the onboarding console, choose GitHub repo on the Import step and give the repository as owner/name or its github.com address, with a branch if it is not the default one. A private repository needs a fine-grained token with read access to its contents. The token is used for that one read and never stored, and the repository is read in memory and not kept.

  • If the repository holds an OpenAPI document (JSON or YAML), that document is imported.
  • Otherwise its HTTP routes are drafted into one. It reads Express, Fastify, NestJS, FastAPI, Flask, ASP.NET and Spring. Dependencies, build output and tests are skipped.
  • A drafted operation has a method and a path and nothing else. You still choose which ones to include, describe each one, and say whether it reads or changes data. Nothing is decided from the code, and the verb only suggests.
  • If your framework serves its own description (FastAPI's /openapi.json, for example), the console says so. That document knows request bodies and types a route scan cannot see, so it is the better input.

A repository is never required: an OpenAPI file, an MCP server or a single endpoint does the same job.

1. Import only what the assistant should use

Most documents describe sign-in, uploads and administration alongside the operations a visitor needs. Declare the ones the assistant may use and leave the rest out with skipUndeclared. In the onboarding console this is the Include checkbox on each operation, and the console sends each operation you leave out as "skip": true instead, so every operation of the document is either declared or skipped by name.

{
  "baseUrl": "https://api.example.com",
  "document": { "…": "your OpenAPI document, unchanged" },
  "skipUndeclared": true,
  "operations": {
    "get_mechanics_search_by_location": { "is_write": false, "description": "…" },
    "get_problem_categories": { "is_write": false, "description": "…" }
  }
}

An operation left out is not imported at all, so it cannot be called; it never becomes a read by default. The response lists what was left out under skipped. Start with the reads that answer your customers' most common questions. Add writes later, one at a time, each with its risk.

2. Say when to use it, not only what it is

A summary such as "Search mechanics" tells the model what the operation is called. It needs to know when to choose it over the others and what comes back.

WeakUseful
Search mechanicsFind repair shops near a point, nearest and best first. Use it whenever the visitor names a place, a district or "near me". Results carry distance, travel time, rating, phone and services.
Get mechanicFull details of one repair shop by id: phone, address, rating, services and car brands. There is no opening-hours information.

Saying what the operation cannot answer ("there is no opening-hours information") stops the model inventing an answer and lets it say so instead. The import warns about any description under 40 characters (short_description).

3. Say where each id comes from

An id the model has to supply is useless unless it knows which other operation returns it, and some APIs care which kind of id it is.

"parameters": {
  "problemCategoryIds": {
    "description": "One service category id from get_problem_categories: a child id, because a parent id only matches shops tagged with exactly that parent. Leave it out if the visitor named no service."
  }
}

Without that sentence, the model passed the parent category and found nothing. The import warns about an id with no description (undescribed_id).

4. Take away the inputs the model should not fill

The model fills the inputs it is offered. Offered an optional car-brand filter, it sent 0, then 1, and silently narrowed every search to one brand. Either describe exactly when to send an optional input, or take it away:

"parameters": {
  "carIds": { "omit": true },
  "skip": { "omit": true }
}

An omitted input is not offered to the model and never sent. Only optional inputs can be omitted; an input your API requires stays. The import warns about optional, undescribed numbers (optional_number) and other optional inputs (undescribed_optional).

5. Require what the answer depends on

If an operation only makes sense with an input, make the model supply it:

"parameters": {
  "userLat": { "required": true, "description": "Latitude of the place the visitor named." },
  "userLng": { "required": true, "description": "Longitude of the place the visitor named." }
}

Where the value comes from matters as much as requiring it. If your visitors name districts rather than coordinates, upload the district coordinates as a knowledge document so the model has somewhere to look them up.

6. Keep it next to the read and write decision

Everything above lives in the declaration, beside is_write, so your OpenAPI document stays as it is and a new version of it can be imported again with the same file. A declaration that names an operation or an input the document no longer has is refused, so a rename on your side shows up as an error instead of a capability that quietly disappeared.

Checklist

  • Every included operation says when to use it and what it returns, in your customers' terms.
  • Every id says which operation returns it.
  • Every optional input is either described with when to send it, or omitted.
  • Inputs the answer depends on are required.
  • warnings on the import response is empty, or each one is a choice you made.
  • Try your customers' real questions, in the forms they ask them, on the test step before you embed.