API
Gairė's API lets a program of yours see your watches and hear about each change as soon as a check finds it. Gairė's Zapier app is built on it.
The API reads and listens. It does not add or change watches: in Gairė, a person sees what a watch will read and says yes before it starts.
Signing in
The API uses OAuth 2.0: the authorization code flow, with PKCE (S256).
- Server details:
https://gaire.lt/.well-known/oauth-authorization-server - Registering a client:
https://gaire.lt/oauth/register(dynamic client registration). Clients are public: no client secret, PKCE instead. - Scopes:
gaire:readto see watches and changes;gaire:writealso to subscribe an address and to stop it.
The person signs in to Gairė and sees which program is asking, and for what, before saying yes. Send the token with each request:
Authorization: Bearer <access token>
An access token lasts 1 hour and a refresh token 90 days. Each refresh gives a new refresh token, good for another 90 days, so keep the new one. If an old refresh token is used more than a minute after it was replaced, the sign-in ends and the person signs in again. This stops a stolen token from being used.
Endpoints
All of them are under https://gaire.lt/api/v1 and answer in JSON.
GET /me
account_id, email (masked, as m•••@example.com), client (the program's name) and watch_count.GET /watches
alert_rule is a watch's rule on a price or a word (only rows below or above a price, or whose name mentions a word, are told), or null.{
"watches": [
{ "id": "…", "name": "Laptops on shop.lt", "url": "https://shop.lt/laptops", "status": "active", "alert_rule": { "price_below": 800 } }
]
}POST /hooks
gaire:write.{ "url": "https://example.com/gaire", "watch_id": "…" }Leave out watch_id to hear about every watch on the account. The answer, 201, gives the subscription's id and its secret. Subscribing the same address and watch again gives back the same subscription.
{ "id": "…", "url": "https://example.com/gaire", "watch_id": "…", "secret": "…" }DELETE /hooks/{id}
204. Needs gaire:write.GET /changes
?watch_id=… narrows them to one watch, and ?limit= sets how many (3 if left out, at most 25). It is there for testing, to see what a hook will receive; here each change's url is your changes page on Gairė.What a hook receives
One POST for each change, with the same JSON body a webhook gets: the five fields, then the watch, when the change was found, and every thing that changed with its old and new values. It is signed and delivered the way a webhook is: the same two headers, the same check, and an answer within 5 seconds. The body and each field are on the Webhooks page.
A hook differs from a webhook recipient in these ways:
- It needs no six-digit code.
- It does not take one of your 5 recipients.
- It is sent at any hour, whatever your quiet hours.
- It belongs to the program that made it: disconnect the program and its hooks stop.
- Each account can have up to 20 of them. One is dropped when its address answers
410, or after 10 failed sends in a row.
Hooks count with webhooks: on the free plan, up to 300 a month are sent between them. As for every recipient, one watch sends to one address at most once in 30 minutes.
Errors
An error answers with a status and a JSON body:
{ "error": { "code": "cap", "message": "…" } }400: the address isn'thttps://on the public internet (bad_url), the watch isn't yours (unknown_watch), or the request is malformed.401: there is no token, or it has expired. TheWWW-Authenticateheader says where to sign in.403: the token lacks the scope (insufficient_scope).404: there is no such watch, subscription or endpoint on this account.409: the account already has 20 subscriptions (cap).
Questions: maksim@gaire.lt