AperioFlow Start free

Docs

Error reporting

AperioFlow groups the errors your apps report into issues, next to the work in the same project. Reporting is one HTTP request.

Find your ingest URL

Open a project and choose Set up reporting. You get an ingest URL of the form https://app.aperioflow.co.uk/ingest/<key>, with ready-made snippets and a button to rotate the key.

The URL needs no API key and allows any origin, so you can use it in browser and mobile code. Anyone who has the URL can add events to that project (and only that project), so rotate the key if it leaks. A key is limited to 300 events a minute.

From a browser

<script>
  (() => {
    const endpoint = 'https://app.aperioflow.co.uk/ingest/YOUR_KEY';
    const send = (type, message, stack) =>
      navigator.sendBeacon(endpoint, JSON.stringify({ type, message: String(message), stack, url: location.origin + location.pathname, platform: 'web' }));
    addEventListener('error', (e) => send(e.error?.name || 'Error', e.message, e.error?.stack));
    addEventListener('unhandledrejection', (e) => send(e.reason?.name || 'UnhandledRejection', e.reason?.message ?? e.reason, e.reason?.stack));
  })();
</script>

From Node

async function reportError(err, extra = {}) {
  await fetch('https://app.aperioflow.co.uk/ingest/YOUR_KEY', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: err.name,
      message: err.message,
      stack: err.stack,
      platform: 'node',
      environment: process.env.NODE_ENV || 'production',
      ...extra,
    }),
  }).catch(() => {}); // reporting must never break the app
}

process.on('uncaughtException', (err) => reportError(err));
process.on('unhandledRejection', (reason) => reportError(reason instanceof Error ? reason : new Error(String(reason))));

From Python

import requests, traceback

def report_error(exc, request=None):
    requests.post("https://app.aperioflow.co.uk/ingest/YOUR_KEY", timeout=2, json={
        "type": type(exc).__name__,
        "message": str(exc),
        "stack": "".join(traceback.format_exception(exc)),
        "url": request.build_absolute_uri().split("?")[0] if request else None,
        "platform": "django",
        "environment": "production",
    })

From anything else

curl -X POST https://app.aperioflow.co.uk/ingest/YOUR_KEY \
  -H "Content-Type: application/json" \
  -d '{"type":"TypeError","message":"Invoice 42 not found","url":"https://app.example.com/invoices/42"}'

What you can send

Only message is required. The other fields are type, level (fatal, error (the default), warning or info), url, route, stack, platform, environment, release, user_agent, fingerprint, occurred_at and context (a JSON object of up to 20 KB).

Send as little personal data as you can: none in message, url, stack or context, and no passwords or tokens anywhere. Leave query strings and fragments out of url. Events are kept for your plan's history period (14 days on Free, 90 days on Solo, 12 months on Team), then deleted.

How errors are grouped

Events with the same fingerprint share one issue. If you do not send a fingerprint, it is built from the error type, the first line of the message with numbers, UUIDs and email addresses masked (so Invoice 42 not found and Invoice 97 not found are one issue), and the route: the path only, with ids replaced, so /invoices/42/edit?tab=lines becomes /invoices/:id/edit. Send route to set it yourself.

The same error on two routes makes two issues. A resolved issue that happens again is reopened. An ignored issue stays ignored but keeps counting. The latest 50 events of each issue are kept, and how long events are kept depends on your plan.

Useful links on an issue

Put context.links on an event, as [{"label": "Deploy", "url": "https://..."}], and the issue shows them as links. Only http and https addresses are shown.

Start free All the docs

Built for the people who write the code and answer the phone when it breaks.

Run from the UK. Hosted in France.

UK time --:--

Already in?Open the app Start free today
View our policies