{"info":{"_postman_id":"7c8e2a10-4b3f-4d91-9c2a-0b1c2d3e4f50","name":"Hookest API","description":"Public Hookest API. Import this file in Postman (Import > Link or file).\nDocs: https://api.hookest.com/docs\nSet collection variable api_key to your hk_live_… key. Query-string keys are rejected.\nPredict → Score a clip: 1 Get upload link (no file). 2 Upload the clip (Body → binary, no Bearer). 3 Get prediction. If still awaiting_upload after a 2xx upload, Retry start.\nOpenAPI: https://api.hookest.com/openapi.json","schema":"https://schema.postman.com/json/collection/v2.1.0/collection.json"},"auth":{"type":"bearer","bearer":[{"key":"token","value":"{{api_key}}","type":"string"}]},"variable":[{"key":"base_url","value":"https://api.hookest.com"},{"key":"api_key","value":"hk_live_YOUR_KEY"},{"key":"hook_id","value":"hook_9f2a1c0b4e7d4a1f8c3e2d1b0a9f8e7d"},{"key":"upload_id","value":"upl_3c91a0b4e7d4a1f8c3e2d1b0a9f8e7d0"},{"key":"upload_url","value":""},{"key":"prediction_id","value":"pred_7b3e0c1d2e3f4a5b6c7d8e9f0a1b2c3d"},{"key":"webhook_url","value":"https://customer.example/hookest"}],"item":[{"name":"Wallet","item":[{"name":"Get wallet","request":{"method":"GET","header":[],"url":"{{base_url}}/v1/wallet","description":"Credits: 0. Current monthly and top-up balances. monthly_period_end is when the monthly bucket resets.\n\nDocs: https://api.hookest.com/docs/wallet"},"response":[]}]},{"name":"Hooks","item":[{"name":"Categories","request":{"method":"GET","header":[],"url":"{{base_url}}/v1/hooks/categories","description":"Credits: 0. Slugs for list hooks . English name is a label; pass slug as category . Needs hooks:read on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/hooks/categories"},"response":[]},{"name":"Search","request":{"method":"GET","header":[],"url":"{{base_url}}/v1/hooks/search?q=first%20three%20seconds","description":"Credits: ceil(rows / 25). Lean hook page matching q in the hook text. q is required. Page size is 50. Pass next_cursor back as cursor . The request reserves 2 credits, then settles ceil(rows / 25) . An empty page settles 0. A wallet with 1 credit still gets 402 on reserve. Needs hooks:read on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/hooks/search"},"response":[]},{"name":"List","request":{"method":"GET","header":[],"url":"{{base_url}}/v1/hooks/list?category=education-schools","description":"Credits: ceil(rows / 25). Lean hook page for one category slug. category is required. Unknown slug is 422 . Slugs come from list categories . Page size is 50. Pass next_cursor back as cursor . The request reserves 2 credits, then settles ceil(rows / 25) . An empty page settles 0. A wallet with 1 credit still gets 402 on reserve. Needs hooks:read on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/hooks/list"},"response":[]},{"name":"Get a hook","request":{"method":"GET","header":[],"url":"{{base_url}}/v1/hooks/{{hook_id}}","description":"Credits: 1. Rich record for one hook. Unknown or malformed ids return 404 . Needs hooks:read on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/hooks/get"},"response":[]}]},{"name":"Predict","item":[{"name":"Score a clip","description":"Get a link, send the MP4, then poll. The file does not go to api.hookest.com.","item":[{"name":"Get upload link","request":{"method":"POST","header":[],"url":"{{base_url}}/v1/predict/upload","description":"Step 1 of 3. Empty POST — no file, no JSON. Saves upload_url, upload_id, and prediction_id on the collection. Next: Upload the clip. Docs: https://api.hookest.com/docs/predict/upload"},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["const j = pm.response.json();","if (j.upload_id) pm.collectionVariables.set(\"upload_id\", j.upload_id);","if (j.upload_url) pm.collectionVariables.set(\"upload_url\", j.upload_url);","if (j.prediction_id) pm.collectionVariables.set(\"prediction_id\", j.prediction_id);"]}}]},{"name":"Upload the clip","event":[{"listen":"prerequest","script":{"type":"text/javascript","exec":["const url = (pm.collectionVariables.get(\"upload_url\") || \"\").trim();","if (!url.startsWith(\"http\") || url.includes(\"api.hookest.com\") || url.includes(\"/v1/predict\")) {","  throw new Error(\"Run Get upload link first. This request uses the upload_url it saved — not api.hookest.com.\");","}"]}}],"request":{"auth":{"type":"noauth"},"method":"PUT","header":[{"key":"Content-Type","value":"video/mp4"}],"body":{"mode":"file","file":{"src":""}},"url":"{{upload_url}}","description":"Step 2 of 3. Body → binary → Select File → pick a .mp4 (max 40 MB). Auth is off. A 2xx means the file arrived — not that scoring started. Then Get prediction. queued = 20 credits reserved. If status stays awaiting_upload, run Retry start. 32s is checked after the file lands. If upload_url is empty or older than 5 minutes, run Get upload link again."},"response":[]},{"name":"Get prediction","request":{"method":"GET","header":[],"url":"{{base_url}}/v1/predict/{{prediction_id}}","description":"Credits: 0. Job status. Id is pred_ plus 32 hex characters — returned by Get upload link . Another key’s id, or a job older than 24 hours, is 404 . After the link, status is awaiting_upload — no credits yet. After Upload the clip , GET again: queued or running means the file was consumed and 20 credits are reserved. If it is still awaiting_upload , the file may have landed without a reserve (credit miss, busy, daily cap) — call Retry start . Keep polling while awaiting_upload , queued , or running . Stop on succeeded or failed . Polling stays free even if a webhook is configured. The webhook body is this same JSON. Needs predict:write on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled . Failed\n\nDocs: https://api.hookest.com/docs/predict/get"},"response":[]}]},{"name":"Retry start","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{base_url}}/v1/predict","body":{"mode":"raw","raw":"{\n  \"upload_id\": \"{{upload_id}}\"\n}","options":{"raw":{"language":"json"}}},"description":"Credits: 0 or 20. Not the happy path. Use this only if Get prediction stays awaiting_upload after a 2xx Upload the clip — credit miss, busy, or daily cap. The same upload_id retries the reserve. If the job is already queued , this returns that prediction and charges 0. upload_id is upl_ plus 32 hex characters from Get upload link . Duration is read from the file. Do not attach the MP4 to this request. Needs predict:write on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled . This call reserves 20 only when consume has not already reserved. Refunded if the job expires or errors.\n\nDocs: https://api.hookest.com/docs/predict/create"},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["const j = pm.response.json();","if (j.id) pm.collectionVariables.set(\"prediction_id\", j.id);"]}}]}]},{"name":"Webhooks","item":[{"name":"Get webhook","request":{"method":"GET","header":[],"url":"{{base_url}}/v1/webhooks","description":"Credits: 0. Account webhook, not per request. One URL and one secret sign every delivery for this account only — another account’s jobs never land here. Set the URL on the Hookest site (API page). These HTTP routes are optional if you already saved it there. Polling on get prediction stays on and stays free. configured is true only when an HTTPS URL is active. A disabled endpoint returns url: null and configured: false . Plan loss does that automatically. The raw secret is never returned here — it is shown once on the site, or on create / rotate . Needs predict:write on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/webhooks"},"response":[]},{"name":"Set webhook","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{base_url}}/v1/webhooks","body":{"mode":"raw","raw":"{\n  \"url\": \"{{webhook_url}}\"\n}","options":{"raw":{"language":"json"}}},"description":"Credits: 0. Optional. The usual path is the Hookest site API page. Use this route when there is no browser. First create returns secret once ( whsec_… ). Later PUTs update the URL and do not rotate the secret. Only https . Loopback, private, and link-local hosts are rejected. Deliveries do not follow redirects. Delivery v1 is HMAC-SHA256 of {t}.{body} with the account secret. body is the same JSON as get prediction . Failed deliveries retry 10s → 1m → 5m → 30m → 2h, then stop. Needs predict:write on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/webhooks/put"},"response":[]},{"name":"Disable webhook","request":{"method":"DELETE","header":[],"url":"{{base_url}}/v1/webhooks","description":"Credits: 0. Turn the account URL off. Pending retries for this account stop. Polling on get prediction stays on. Plan loss or suspension does this automatically — you do not have to DELETE. After that, GET returns configured: false and nothing is posted to the old URL. Re-subscribe, then set the URL again. No webhook configured is 404 . Needs predict:write on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/webhooks/delete"},"response":[]},{"name":"Rotate secret","request":{"method":"POST","header":[],"url":"{{base_url}}/v1/webhooks/rotate","description":"Credits: 0. Issue a new whsec_… . Shown once. Previous secret stops verifying new deliveries. No webhook configured is 404 . Needs predict:write on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/webhooks/rotate"},"response":[]},{"name":"Test webhook","request":{"method":"POST","header":[],"url":"{{base_url}}/v1/webhooks/test","description":"Credits: 0. POST a synthetic prediction.succeeded to your URL, signed with the same account secret. The fake id is pred_00000000000000000000000000000000 — it is not a real job and cannot read another account’s prediction. Use this to check signature verification before production traffic. No webhook configured is 404 . Needs predict:write on the key. This is a permission on the key, not a request header. Missing it returns 403 api_not_enabled .\n\nDocs: https://api.hookest.com/docs/webhooks/test"},"response":[]}]}]}