Highlights
Two changes this week affect what comes back from your requests. Response bodies stop arriving as mojibake on non-UTF-8 sites, and validate-accepted non-200 responses finally count as success instead of failure. We also locked down a couple of security holes on the customer API.
What's New
Non-UTF-8 pages return readable text on Single
If you call Single on Bulgarian forums, Chinese e-commerce, Japanese news, or anything that ships windows-1251, GBK, Big5, or Shift_JIS, the response body used to come back as broken bytes. The underlying HTTP layer hard-coded a UTF-8 decoder, so a Cyrillic page arrived as промоции and there was no way to recover the original on your end.
This is fixed at the request layer. Single now detects the source charset (via Content-Type header, then <meta charset>, then <meta http-equiv>) and transcodes to UTF-8 before the body reaches your JSON envelope. And UTF-8 or ASCII pages pass through untouched. Binary content like images or PDFs is never decoded. If you want the raw bytes, returnBuffer: true still hands back the original buffer.
Default-on. No flag to flip. Pages that worked before still work; pages that returned garbage now return readable text. Browser users don't have to think about this either; Chromium decodes charsets natively.
validate rules now drive success classification
When you set validate on a request (for example validate.status.accept = [200, 403]), the request engine already honored your rule and resolved the response without an error. But our outcome classifier upstream ignored your rule and bucketed anything that wasn't a literal 200 as application_fail. Two consequences: your accepted-403 showed up as a failure in the Dashboard, and since only success is billable, those delivered responses were also unbilled.
The classifier now respects what validate declared. Requests with validate count as success whenever the engine resolved them without error, whatever the status. Requests without validate behave the same as before (success on 200 only), so the legacy path is untouched.
Forward-only fix: historical rows keep their stored outcome, new requests classify correctly. So if you've been seeing App Fail on responses you knew were valid, this is why.
Security hardening on the customer API
We shipped Wave 0 of a security review across the customer API:
- CORS is now restricted to
https://foura.ai. The previous setting reflected any origin while sending credentials, which is the standard CSRF setup. Same-origin browser calls and your server-side API calls aren't affected. - The metrics route behind your Activity timeline used to accept a free-form
outcomefilter that flowed straight into the query. It's now allowlisted to known outcome values. Not exploitable from a normal account, but worth closing.
Neither changes any API contract. You won't notice them during normal use. But finding a problem and closing it before anyone notices still deserves saying out loud.
Activity labels match the Overview
Small one. The Activity table in your Dashboard used to render raw outcome strings like Application_fail while the Overview chips, donut, and timeline showed friendly labels (App Fail) and color-coded each outcome. Same data, two presentations. They're now in sync, both reading from the same label and color map, so a row's status looks the same wherever you check it.
Numbers
This week brings no new latency or success-rate numbers. Most of this is correctness and security work where the right metric is "stop being wrong" rather than "go faster". Next week's digest should have throughput data from a few things in flight.