Back

    Buyamia Directory API

    v1.1.0

    Authentication & Scopes

    API key header

    Every /v1/* request must send the key in the X-API-Key header. There is no OAuth, no bearer token, and no cookie session on this API.

    curl "https://rjpxllsycikfabazvkdh.supabase.co/functions/v1/directory-api/v1/categories" \
      -H "X-API-Key: your-api-key-here"
    ConditionStatusBody
    No X-API-Key header401{ "error": "Missing X-API-Key header" }
    Unknown or deactivated key401{ "error": "Invalid API key" }
    expires_at in the past401{ "error": "API key expired" }
    Per-minute limit exceeded429{ "error": "Rate limit exceeded" }
    Key lacks the required scope403{ "error": "Forbidden: <scope> required" }

    Complete scope listChanged in 1.1.0

    A key carries an array of scopes. The wildcard * satisfies every check. These 15 scopes plus * are everything the API enforces. Since 1.1.0 the three internal-visibility scopes can be granted explicitly on a key; the access rules themselves are unchanged.

    ScopeUnlocksSensitivity
    businesses.readGET /v1/businesses, /v1/businesses/:id, /v1/businesses/slug/:slug, sub-resourcesPublic data
    businesses.writePOST /v1/businesses, PUT /v1/businesses/:id, POST /v1/businesses/:id/performanceMutates the directory
    businesses.internal.readDraft / admin_only businesses, and honours ?visibility= on list + searchInternal — non-public data
    businesses.assets.readGET /v1/businesses/:id/assets (may include internal documents)Internal — non-public data
    products.readGET /v1/products, /v1/products/:id, /v1/businesses/:id/products, price historyPublic data
    products.writePOST /v1/products, PUT /v1/products/:idMutates the catalog
    products.internal.readNon-active / admin_only products on list, detail, business products, price history and searchInternal — non-public data
    categories.readGET /v1/categories, /v1/categories/:id, /v1/product-categoriesPublic data
    regions.readGET /v1/regions, /v1/regions/:id, /v1/countriesPublic data
    searchGET /v1/search, /v1/semantic-search, /v1/hybrid-searchPublic data
    rfq.readGET /v1/rfq, /v1/rfq/:id, /v1/rfq/:id/quotes, /v1/rfq/:id/quotes/:supplierIdBuyer contact data
    rfq.writeCreate RFQs, update RFQs, attach/detach suppliers (also satisfies RFQ list/detail reads)Buyer contact data
    quotes.writePOST /v1/rfq/:id/quotes — record a supplier quotePricing data
    agent.queryThe Agent Gateway (separate base URL)AI surface
    webhooks.managePOST/GET/DELETE /v1/webhooksSystem
    *Everything above, including all internal scopesDangerous — first-party only

    Do not hand out * to third parties. It silently grants the three internal scopes, which expose draft listings and internal business documents. Grant the internal scopes explicitly when a trusted first-party integration genuinely needs them.

    Visibility rules

    • Without businesses.internal.read, business list/detail/slug/search results are filtered to visibility = public, and any ?visibility= query parameter is silently ignored rather than rejected.
    • Without products.internal.read, product results are filtered to status = active AND visibility = public, on list, detail, business products, price history and search.
    • /v1/semantic-search and /v1/hybrid-search force include_internal = false for every caller, including * keys. A caller-supplied include_internal parameter has no effect.
    • GET /v1/businesses/:id/assets requires businesses.assets.read (or *) and returns 403 otherwise.
    • GET /v1/businesses/:id/capabilities applies no visibility filter — any key with businesses.read sees all capability rows for that business. Filter on your side if that matters to you.

    Rate limiting — honest description

    Each key has a rate_limit_per_minute (default 60). The limiter counts requests in a rolling 60-second window in memory, per running function instance. It therefore resets on cold start and is not shared between concurrently running instances.

    Treat rate limiting as best-effort throttling, not a hard contractual guarantee. The effective ceiling can be higher than the configured number under load, and requests can be rejected at 429 sooner than you expect after a burst. Always handle 429 with exponential backoff.

    No X-RateLimit-* response headers are sent. The only signal is the 429 status.

    Request logging

    Every authenticated request is logged (method, path, status, latency, IP) and the key's last_used_at is refreshed. Ask the Buyamia team if you need a usage report for your key.

    Current as of API 1.1.0 — released 7 September 2026