A published endpoint is only useful if callers can shape the query: filter by tenant, change the time window, paginate. Query parameters are the contract for that. Each parameter is declared inline in SQL with a type (String, Int32, DateTime, Array(String), and so on) and an optional default, and Tinybird validates and casts incoming request arguments against that declaration before the query runs.
This pushes a class of bugs out of the application layer. The endpoint refuses malformed input, type mismatches return a 4xx instead of a query error, and the OpenAPI surface generated for the endpoint reflects the same declaration. The pipe is the schema.
Parameters are not string interpolation. They are typed bindings: a {{ String(tenant_id) }} placeholder is a guarantee about what reaches the SQL engine, not a textual substitution.
Typed parameters with a default and an enum
%
SELECT path, count() AS views
FROM pageviews
WHERE tenant_id = {{ String(tenant_id, required=True) }}
AND timestamp >= now() - INTERVAL {{ Int32(hours, 24) }} HOUR
AND device IN {{ Array(devices, 'String', default=['mobile','desktop']) }}
GROUP BY path
ORDER BY views DESC
LIMIT {{ Int32(limit, 10) }}