Skip to main content
reference 3 min read

Personal API keys

Long-lived single-credential bearer tokens for your own scripts.

For your own scripts — anything where the OAuth dance is overkill. An API key is a single-credential bearer token that always acts as you.

When to use API keys vs OAuth

Use API keys whenUse OAuth when
It's a personal script (cron job, jupyter notebook, weekend hack)A multi-tenant integration that touches other users' data
The credential lives on a machine you controlThe credential is shipped to end-user devices
You're OK with the token never expiringYou need users to revoke individually from their side

API keys cannot grant flights:delete. Use OAuth Authorization Code if you need destructive scopes.

Generating a key

  1. Sign in to https://we-fly.cloud.
  2. Open https://we-fly.cloud/developerNew API key.
  3. Pick a name, an expiration (1–365 days, or blank for no expiration), and the scopes you need.
  4. Copy the key (wf_key_…) when it's revealed. It's shown once — if you lose it, revoke the key and generate a new one.

Using a key

curl https://we-fly.cloud/api/v1/flights \
  -H "Authorization: Bearer ${WEFLY_API_KEY}"

Identical to using an OAuth access token. The same scope and rate-limit rules apply.

The key is the entire credential — there is no client_id. Unlike the OAuth flows, an API key isn't tied to a registered application: you don't send a client_id, there's no app to register, and no token exchange. The wf_key_… value already identifies you (the owner) and carries its scopes. Just present it as the Bearer token.

Uploading an IGC with a key

If the key carries flights:write, it can upload flights directly — the simplest possible automated upload for a personal script or a device that can hold one long-lived secret:

curl -X POST https://we-fly.cloud/api/v1/flights/upload \
  -H "Authorization: Bearer ${WEFLY_API_KEY}" \
  -F "file=@2026-07-23-flight.igc"
# → 201 Created (new) or 200 OK (duplicate)
# → { "data": { "hash": "…", "fileName": "…", "isNew": true } }

Note this is the same wf_key_… credential a pilot generates under Profile → Connected Services for instrument/WebDAV and Leonardo uploads — see native-app upload for when to use a key vs the OAuth device flow in a shipped app.

Revoking a key

/developer → click Revoke on the row. Effects are immediate; subsequent requests with the revoked key return 401 invalid_token.

Revoking is irreversible. There's no "unrevoke" — generate a new key if you change your mind.

Best practices

  • Store the key in a secrets manager, not in a checked-in .env file. If your CI logs leak, you don't want the key with them.
  • Set an expiration if the script's life is bounded (e.g., a one-off backfill).
  • Use the narrowest scope set you can. If your script only reads, don't tick flights:write.
  • Rotate before they're compromised. A monthly rotation is a good default for production scripts.

Example: nightly export of new flights

import os, requests, json
from datetime import datetime, timezone

API = "https://we-fly.cloud/api/v1"
KEY = os.environ["WEFLY_API_KEY"]
HEAD = {"Authorization": f"Bearer {KEY}", "User-Agent": "nightly-export/1.0"}

def list_flights(page=1):
    r = requests.get(f"{API}/flights", headers=HEAD, params={"page": page, "pageSize": 100})
    r.raise_for_status()
    return r.json()

def main():
    all_flights = []
    page = 1
    while True:
        body = list_flights(page)
        all_flights += body["data"]
        if page >= body["pagination"]["totalPages"]:
            break
        page += 1
    with open("flights.json", "w") as f:
        json.dump({"exportedAt": datetime.now(timezone.utc).isoformat(), "flights": all_flights}, f, indent=2)

main()