Testing your integration
There is no test mode
Asky has no sandbox, no test keys, and no separate test data, and you do not need them.
There is no sandbox. Developing against your real workspace has the advantage that you are testing against the data shapes you will actually receive rather than fixtures that drift, and reads cannot damage anything.
Be deliberate about the write endpoints, which are real. DELETE on a topic removes the prompts beneath it and the measured history attached to them. Use dry_run=true first: it reports exactly what would be removed and removes nothing.
What you do need to test is how your integration behaves when a request fails. That is what this page covers.
Use a dedicated key for development
Create a key named for the purpose, Local development or CI, rather than reusing a production key. You can then revoke it without touching anything that matters, and usage separates your test traffic from real traffic.
If you work on one brand, restrict the key to it. Development then cannot accidentally read across your whole workspace.
Provoking each error
Every failure mode is reachable deliberately, which means you can test your handling rather than hoping it works.
| To test | Do this | Expect |
|---|---|---|
| Missing credentials | Omit the Authorization header | 401 invalid_token |
| Bad credentials | Send Bearer asky_sk_not_a_real_key | 401 invalid_token |
| Revoked key | Create a key, revoke it, then use it | 401 token_revoked |
| Expired key | Create a key with an expiry a minute away, wait | 401 token_expired |
| Wrong permissions | Request a brand your key is not permitted to reach | 404 not_found |
| Unknown resource | Use a well-formed id that does not exist | 404 not_found |
| Malformed request | Send ?limit=99999 or ?start_date=12-08-2026 | 400 invalid_request |
| Unknown parameter | Send ?engnie=chatgpt | 400 invalid_request |
| Bad cursor | Send ?cursor=nonsense | 400 invalid_cursor |
| Wrong method | Send POST to any endpoint | 405 method_not_allowed |
| Rate limiting | Loop requests past 300 in a minute | 429 rate_limited with Retry-After |
# Unknown parameter, the most common real-world 400
curl -G https://api.askylabs.com/v1/brands \
-H "Authorization: Bearer $ASKY_API_KEY" \
-d limt=10Test the 429 path deliberately at least once. Backoff code is written early, rarely exercised, and discovered to be wrong during the first busy backfill. Provoking it on purpose is far cheaper than finding out later.
What to assert
Retry only what is retryable. A test that a 400 is not retried is as valuable as one that a 429 is. Retry loops over permanent failures burn your budget and delay you noticing the real problem.
Pagination terminates. Walk a list to the end and assert you saw every item exactly once. Assert too that your loop stops on next_cursor: null rather than on a short page, since a page can be short and still have more after it.
Unknown fields are ignored. Add an invented property to a fixture and assert your parser does not throw. Strict parsers that reject unrecognised fields are the single most common cause of an integration breaking on an additive change that nobody else notices.
Empty results are handled. Assert your code does something sensible with zero rows, since a valid request can legitimately return none.
No key in your logs. Assert your error paths log the request_id and never the Authorization header.
Recording real responses
For fast, offline tests, record real responses once and replay them. Redact the key from any recording before it is committed, since request headers are captured by most recording libraries by default.
Refresh recordings periodically. Fixtures that never change stop reflecting the API, and a test suite passing against a two-year-old shape is a false sense of safety.
In CI
Store the key in your CI provider’s secret store, never in the workflow file, and confirm your provider masks it in logs.
Keep CI traffic light. A test suite that walks full result sets on every commit competes with production for the same rate limit if it shares a key, which is another reason CI should have its own.
If your CI runs on forked pull requests, make sure secrets are not exposed to them. Most providers withhold secrets from forks by default; confirm rather than assume.
Before you go live
- A
401on your scheduled job raises an alert to a person, not just a log line. - Backoff has been exercised against a real
429. - The job alerts when it does not run, not only when it errors.
- Your key’s expiry, if it has one, is on a calendar or the reminder emails reach a monitored address.
- You can find a request in your own logs by
request_id.