For the complete documentation index, see llms.txt. This page is also available as Markdown.

Ask filters

POST https://api.tiny.plus/v2/ai/filter

Turns a plain-English question into the query parameters for a record list. Send "won projects over 50k that Maya is on", get back record_status=won, fee_value=>50000 and staff=<Maya's id>, ready to put on GET /v2/projects.

It doesn't read or return any records itself — you run the list. It reads your account's fields, labels, statuses, tags and team so the words map to your setup.

This is the same "Ask" feature as the search box in tiny+. It needs the Pro plan (or a trial), with Ask switched on in Account Settings → AI Usage. Where it's off, the endpoint still answers 200, telling you to fall back to a plain search — see When Ask falls back. A token's user can check integrations.ai_ask in /me first.

Request

Send a JSON body:

curl -X POST "https://api.tiny.plus/v2/ai/filter" \
  -H "Authorization: Bearer {{user_access_token}}" \
  -H "Content-Type: application/json" \
  -d '{
        "flavour": "projects",
        "q": "won projects over 50k this year that Maya is on, biggest first"
      }'
Field
Description

flavour

Required. The record type to build a filter for, such as projects or contacts.

q

Required. The question, 1 to 500 characters.

current_filters

Optional. A filter already in force, as a {field: value} object in the list grammar. Ask refines it and returns the whole new set — this is how you follow up "…and only the ones I own".

client

Optional label for your integration, recorded in tiny+'s AI log.

Response

Field
Meaning

query

The parameters to send to GET /v2/{flavour}: field conditions, tags_{id}, custom_{id}, me, staff and sort. {} when nothing in the question could be used.

explanation

One sentence describing the filter, in the account's own labels.

assumptions

The readings Ask had to make — worth showing the user so they can correct one.

unresolved

Parts it couldn't map, each {phrase, reason} — for example a name that matches no team member.

confidence

high (a clean mapping), medium (it made assumptions), or low (something's unresolved, or the query is empty).

chips

The filter as a list of {key, label}, for showing it back to the user.

source

model for a fresh answer, cache for a repeat within the account.

Applying the filter

  1. If the response has a reason, run the fallback search (below).

  2. Otherwise, show the user the explanation, chips, assumptions and anything unresolved.

  3. When query isn't empty and the confidence is good enough for you, send its entries as query parameters on GET /v2/{flavour}. URL-encode the values — >, <, !, spaces and | all need it — and add your own limit, start and fields.

  4. To refine, send that query back as current_filters with the next question.

When Ask falls back

If Ask is switched off, over a limit, or the model can't help, you still get 200 — with query: null and a reason, plus a fallback telling you to run a plain search:

reason is one of disabled (Ask isn't on for the account), capped (a rate limit is used up), timeout, overloaded, refusal, invalid or error. In every case, run GET {fallback.endpoint}?q={fallback.q}.

A question the model understands but can't turn into a filter — "what's the weather in Paris?" — is not a fallback. It comes back 200 with query: {}, confidence: low and the phrase under unresolved. Branch on reason (or query === null) to tell the two apart.

Limits and errors

Ask draws on your account's AI allowance, with a per-minute limit as well. When either is used up, the response is the capped fallback above. The numbers are on the Fair use page.

Case
Response

flavour missing, not a string, or not an API record type

400 {"error": "'flavour' must name a record type this account exposes to the API."}

q missing or blank

400 {"error": "'q' is required."}

q over 500 characters

400 {"error": "'q' is limited to 500 characters."}

current_filters not an object

400 {"error": "'current_filters' must be an object of field=value pairs."}

Missing or invalid token

401, in the OAuth error shape

Last updated

Was this helpful?