Back to home

Mite developer docs

SDK, REST API, authentication, errors, rate limits, and webhooks

Mite is in-app feedback and release notes for Expo and React Native apps. Your
app sends bug reports, feature requests, and user identities to the Mite API;
you triage them in the Mite dashboard and publish release notes back to the
same app.

This page is the developer index: SDK, REST API, authentication, errors, rate
limits, webhooks, and the machine-readable files that describe all of it.

Quickstart

  1. Create an app in the Mite dashboard and generate
    an API key under Settings → Keys.
  2. Install the SDK in your Expo or React Native app.
  3. Submit your first bug report.
npm install @usemite/mite-sdk
import { Mite, MiteProvider, useBugReport } from '@usemite/mite-sdk'

const mite = new Mite({ apiKey: 'mite_ak_...' })
mite.init()

// Wrap your app once:
<MiteProvider miteInstance={mite}>{/* Your app */}</MiteProvider>

// Then submit bugs from anywhere:
const { submitBugReport } = useBugReport()
await submitBugReport({
  title: 'Something broke',
  description: 'What the user saw',
})

SDK

The React Native and Expo SDK is published as
@usemite/mite-sdk. It wraps every
endpoint below, collects device and app context, and retries safely. Use it
unless you have a reason to call the API directly.

Authentication

Every endpoint except /api/v1/health needs a Mite API key, sent as a bearer
token:

Authorization: Bearer mite_ak_...

Mite API keys are publishable, SDK-style tokens, like a Sentry DSN or a PostHog
project key. They ship inside your mobile binary and anyone can extract them
from a shipped APK or IPA, so they are not secrets. Mite handles abuse with
per-key rate limits and instant revocation instead of secrecy. Never use a Mite
key as a substitute for your own user authentication.

Each key carries scopes:

Scope Grants
read List releases, announcements, feature requests
write Submit bug reports, feature requests, votes, identities

A request with the wrong scope is refused with 403.

Base URL

https://intent-okapi-412.convex.site

API endpoints

Method Path Scope Purpose
GET /api/v1/health none Liveness probe
POST /api/v1/bug-reports write Submit a bug report
POST /api/v1/upload-url write Create a single-use attachment upload URL
POST /api/v1/identify write Create or update an end-user profile
GET /api/v1/releases read List published releases
GET /api/v1/announcements read List active announcements
GET /api/v1/feature-requests read List feature requests
POST /api/v1/feature-requests write Submit a feature request
POST /api/v1/feature-requests/vote write Toggle a vote
GET /api/v1/feature-requests/votes read List one voter's votes
GET /getImage read Fetch a stored attachment

Every path also answers OPTIONS for CORS preflight.

The full request and response schema for each endpoint is published as OpenAPI
3.1 at usemite.com/openapi.json.

Submit a bug report

curl -X POST https://intent-okapi-412.convex.site/api/v1/bug-reports \
  -H "Authorization: Bearer mite_ak_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Crash when opening the Profile tab",
    "description": "The app closes immediately after tapping Profile.",
    "app_version": "1.4.2",
    "device_info": { "model": "iPhone 15", "os": "iOS 18.2" }
  }'
{ "id": "j57...", "status": "NEEDS_TRIAGE" }

Errors

Every failure returns JSON, never HTML. Branch on code, not on error.

{
  "error": "API key is missing the required 'write' scope",
  "code": "forbidden",
  "hint": "Create a key with the write scope in Settings → Keys, or use an existing key that has it."
}
Status code Meaning
400 invalid_request Malformed JSON, or a field failed validation
401 unauthorized Missing, malformed, or unknown API key
402 REPORT_QUOTA_EXCEEDED The plan's monthly report allowance is used up
402 STORAGE_QUOTA_EXCEEDED The plan's attachment storage is used up
403 forbidden The key lacks the scope the endpoint needs
404 not_found No such resource for this application
413 payload_too_large The request body is larger than 256 KB
429 rate_limited Per-key rate limit exceeded
500 internal_error Unexpected server error

A quota refusal is 402, never 429. A 429 tells a client to retry; a
monthly quota cannot improve by retrying, only by changing plan. Quota
responses carry a quota object with limit, used, and resets_at.

Rate limits

Limits are per API key, as a token bucket refilled over one minute. They are
scoped to the key rather than to the application, so one misbehaving key cannot
exhaust the budget of a sibling key on the same app.

Endpoint Policy Requests per minute
/api/v1/bug-reports bug-reports 60
/api/v1/upload-url upload-url 30
/api/v1/identify identify 120
/api/v1/releases releases 120
/api/v1/announcements announcements 120
/api/v1/feature-requests (GET) feature-requests-list 120
/api/v1/feature-requests (POST) feature-requests-create 30
/api/v1/feature-requests/vote feature-requests-vote 60
/api/v1/feature-requests/votes feature-requests-votes 120
/getImage images 300

/api/v1/health is unauthenticated and unmetered.

Rate limit headers

Every metered response carries your remaining quota, so you can throttle before
you are refused rather than discovering the limit by hitting it. The fields
follow the IETF RateLimit header fields draft.

RateLimit-Policy: "releases";q=120;w=60
RateLimit: "releases";r=118;t=1
RateLimit-Limit: 120
RateLimit-Remaining: 118
RateLimit-Reset: 1
Field Meaning
RateLimit-Policy The quota in force: q requests per w seconds
RateLimit What is left for you: r requests, t seconds until the quota is whole
RateLimit-Limit Superseded plain-integer form of q
RateLimit-Remaining Superseded plain-integer form of r
RateLimit-Reset Superseded plain-integer form of t

The three plain-integer fields are from an earlier draft and are no longer part
of the specification. Mite still sends them because most deployed HTTP clients
read them; prefer RateLimit and RateLimit-Policy in new code.

All of them are named in Access-Control-Expose-Headers, so a browser can read
them cross-origin.

A refused request returns 429 with Retry-After in seconds, alongside
RateLimit reporting r=0. When both are present, Retry-After is
authoritative.

HTTP/1.1 429 Too Many Requests
Retry-After: 1
RateLimit-Policy: "bug-reports";q=60;w=60
RateLimit: "bug-reports";r=0;t=60

Rate limits on usemite.com

The machine-readable documents on usemite.com carry RateLimit-Policy and are
metered per client at 600 requests per minute. Those documents are
/openapi.json, /api/v1, /.well-known/api-catalog, /agents.md,
/llms-full.txt, and the JSON errors under /api/.

Those responses carry no live RateLimit field. They are publicly cacheable,
and a shared cache would otherwise hand one client's remaining quota to the
next. A refusal is no-store, so it carries the full set.

Webhooks

Mite posts outgoing notifications to Discord and Slack. Add an incoming webhook
URL under Settings → Webhooks in the dashboard and choose which events fire:

  • a new bug report arrives
  • a bug report changes status
  • a new feature request arrives

Discord URLs must start with https://discord.com/api/webhooks/ and Slack URLs
with https://hooks.slack.com/services/. Mite does not accept inbound webhooks.

Machine-readable resources

Resource What it is
/openapi.json OpenAPI 3.1 description of the Mite API
/api/v1 Mite API discovery document
/.well-known/api-catalog Mite API catalog, as an RFC 9727 linkset
/agents.md When to use Mite, and how to make the first call
/llms.txt Index of Mite's pages for agents
/llms-full.txt Every Mite page as one Markdown document
/sitemap.xml Every public page
/docs.md This page as Markdown

Every public page also answers Accept: text/markdown with a Markdown
rendition of itself, and advertises it with a Link: rel="alternate" header.

Support

Open an issue on github.com/usemite/mite-sdk
for SDK problems, or use the in-app feedback widget in the Mite dashboard.