Ga naar de inhoud
Terug naar Nymbot

Kennisbank Ontwikkelaars

Balance, top-ups en sleutels

Controleer wat je hebt, top up over Lightning, top up automatisch uit je eigen portemonnee, zie wat elke aanvraag kost en beheer sleutels uit code.

Controleer de balans

Beide van uw balans, en hoeveel van deze sleutel de hoes wordt gebruikt.

GET https://nymbot.ai/api/v1/credits/balance Je hebt een API sleutel nodig. POST Het werkt ook voor klanten die het verwachten.

balance is de twee saldi samen in dollars tegen de huidige Bitcoin-prijs, voor tools die een enkel getal verwachten (null De rest is in credits en sats, dat is hoe de saldi daadwerkelijk worden bewaard. key beschrijft de sleutel die is gevraagd.Een sleutel die zijn cap heeft bereikt, kan nog steeds het saldo controleren.

Reactie

{
  "balance": 49.18,
  "balance_sats": 42037,
  "standard": { "credits": 120.4, "sats": 1204 },
  "pro": { "credits": 408.33, "sats": 40833 },
  "key": {
    "id": "4f0c9a1be27d3856",
    "name": "laptop scripts",
    "limit_sats": 20000,
    "period_used_sats": 3412,
    "total_used_sats": 18230,
    "reset_period": "monthly",
    "reset_at": "2026-10-01T00:00:00Z"
  }
}
StatusWanneer
401De sleutel is ontbreken, onbekend, ingetrokken of verlopen.

cURL

curl https://nymbot.ai/api/v1/credits/balance \
  -H "Authorization: Bearer $NYMBOT_API_KEY"

Python

import os
import requests

res = requests.get(
    "https://nymbot.ai/api/v1/credits/balance",
    headers={"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]},
)
balance = res.json()
print(balance["standard"]["sats"], balance["pro"]["sats"])

JavaScript

const res = await fetch("https://nymbot.ai/api/v1/credits/balance", {
  headers: { "Authorization": "Bearer " + process.env.NYMBOT_API_KEY },
});
const balance = await res.json();
console.log(balance.standard.sats, balance.pro.sats);

Betalingsmethoden

Hoe je kunt top, en de grenzen. bliksem is de enige methode.

GET https://nymbot.ai/api/v1/topup/payment-methods Geen sleutel nodig.

Een top-up is 10 tot 1.000.000 sats; een Pro-top-up moet ten minste één Pro-credit kopen, dus het begint bij 100 sats. bulk_bonus Hieronder staat het extra krediet op grotere top-ups, hetzelfde als in de app: 10%, 15% of 20% meer op standaard top-ups van 500, 1.000 of 5.000 sats, en op Pro top-ups van 5.000, 10.000 of 50.000 sats.

Reactie

{
  "supported_methods": [
    {
      "method": "btc-lightning",
      "display_name": "Bitcoin Lightning",
      "supported_currencies": ["SATS", "USD", "BTC"],
      "limits": {
        "SATS": { "min": 10, "max": 1000000 },
        "USD": { "min": 0.02, "max": 1170 },
        "BTC": { "min": 1e-7, "max": 0.01 }
      },
      "tiers": ["standard", "pro"],
      "default_tier": "pro",
      "tier_min_sats": { "standard": 10, "pro": 100 },
      "sats_per_credit": { "standard": 10, "pro": 100 },
      "bulk_bonus": [
        { "bonus": 0.1, "standard_sats": 500, "pro_sats": 5000 },
        { "bonus": 0.15, "standard_sats": 1000, "pro_sats": 10000 },
        { "bonus": 0.2, "standard_sats": 5000, "pro_sats": 50000 }
      ]
    }
  ]
}

cURL

curl https://nymbot.ai/api/v1/topup/payment-methods

Python

import requests

methods = requests.get("https://nymbot.ai/api/v1/topup/payment-methods").json()
print(methods["supported_methods"][0]["limits"])

JavaScript

const methods = await (await fetch("https://nymbot.ai/api/v1/topup/payment-methods")).json();
console.log(methods.supported_methods[0].limits);

Toppen boven de bliksem

Maak een Lightning-factuur die krediet toevoegt aan de nym waaraan de sleutel behoort. Controleer het Om het krediet toe te voegen.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Je hebt een API sleutel nodig.

Het veldtypevereistBeschrijving
amountAantalJaHoeveel, in currencyEen heel getal voor sats.
currencyStringnietSATS (Het is een defect) USD of BTCDollars worden omgezet naar de huidige Bitcoin prijs.
tierStringnietpro (de default) of standard: naar welke balans het krediet gaat.

Een standaard credit is 10 sats en een Pro credit 100 sats, plus een bulk bonus; credits zegt wat deze factuur zal toevoegen.

Reactie

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
StatusWanneer
400Een andere methode op de weg (unsupported_method, een onbekende munt (unsupported_currency) of tier, een ontbrekende hoeveelheid, of een bedrag onder het minimum (amount_too_small, meer dan 1 000 000 sats (amount_too_large) of geweigerd door de Lightning wallet (amount_out_of_range).
429Meer dan 60 facturen voor deze nimm, of 120 van dit adres, in een uur (rate_limit_exceeded, met Retry-After).
502Er kan op dit moment geen factuur worden gemaakt (invoice_unavailable, met Retry-After).

cURL

curl https://nymbot.ai/api/v1/topup/create/btc-lightning \
  -H "Authorization: Bearer $NYMBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount": 10000, "currency": "SATS", "tier": "pro"}'

Python

import os
import requests

res = requests.post(
    "https://nymbot.ai/api/v1/topup/create/btc-lightning",
    headers={"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]},
    json={"amount": 10000, "currency": "SATS", "tier": "pro"},
)
invoice = res.json()
print(invoice["payment_request"])

JavaScript

const res = await fetch("https://nymbot.ai/api/v1/topup/create/btc-lightning", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.NYMBOT_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ amount: 10000, currency: "SATS", tier: "pro" }),
});
const invoice = await res.json();
console.log(invoice.payment_request);

Check een top-up

Vraagt of de factuur is betaald en, zodra het heeft, voegt het krediet toe. Checking is wat het krediet, dus na het betalen, controleer tot de status is creditedControleer daarna opnieuw is veilig: de kredietlanding eenmaal, hoe vaak je ook vraagt.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} Je hebt een sleutel nodig van de nym die de factuur heeft gemaakt.

status is pending (nog niet betaald) paid (betaald, maar nog niet gecrediteerd; controleer opnieuw), credited (op uw balans) of expired (niet tijdig betaald).De saldovelden zijn voor de laag waar de factuur bovenop staat.

Reactie

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
StatusWanneer
400De id is niet de 64-karakter-id van de creatieoproep.
404Geen factuur door die id voor je nym (invoice_not_found).

cURL

curl https://nymbot.ai/api/v1/topup/status/$INVOICE_ID \
  -H "Authorization: Bearer $NYMBOT_API_KEY"

Python

import os, time
import requests

headers = {"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]}
url = "https://nymbot.ai/api/v1/topup/status/" + invoice["invoice_id"]

while True:
    status = requests.get(url, headers=headers).json()["status"]
    if status in ("credited", "expired"):
        break
    time.sleep(3)
print(status)

JavaScript

const headers = { "Authorization": "Bearer " + process.env.NYMBOT_API_KEY };
const url = "https://nymbot.ai/api/v1/topup/status/" + invoice.invoice_id;

let status;
do {
  await new Promise((r) => setTimeout(r, 3000));
  status = (await (await fetch(url, { headers })).json()).status;
} while (status !== "credited" && status !== "expired");
console.log(status);

Wilt geschiedenis

Een rij per verzoek: wat het was, welk model, hoeveel tokens en wat het kost. Er worden geen aanwijzingen of antwoorden bewaard, dus geen worden geretourneerd. Rijen worden 90 dagen bewaard, nieuwste eerst. Een sleutel die zijn cap heeft bereikt, kan nog steeds zijn geschiedenis lezen.

GET https://nymbot.ai/api/v1/queries/history — heeft een API-sleutel nodig, die zijn eigen verzoeken ziet, of een Ondertekend verzoek van je nim, die elke sleutel ziet.

Het veldtypevereistBeschrijving
pageDe vraag (query)nietStandaard 1, maximaal 1000; een hogere pagina is 400 invalid_value.
page_countDe vraag (query)nietRaden per pagina. Standaard 20, maximaal 100.
start_date
end_date
De string (query)nietISO 8601 datums of tijden.
modelDe string (query)nietAlleen dit model.
typeDe string (query)nietchat, responses, messages, image, video, speech, transcription of embedding.
all_keysBooleaanse taal (query)nietMet een sleutel: true bevat elke sleutel van dezelfde nym. false.
key_idDe string (query)nietMet een ondertekend verzoek, of met all_keys=trueAlleen deze sleutel.

Reactie

{
  "data": [
    {
      "id": "q_71c4e9a0",
      "timestamp": "2026-09-30T08:12:00Z",
      "model": "anthropic/claude-sonnet-5",
      "type": "chat",
      "input_tokens": 1240,
      "output_tokens": 380,
      "cached_tokens": 0,
      "cost_sats": 16.2,
      "cost_usd": 0.01895,
      "balance": "pro",
      "key_id": "4f0c9a1be27d3856",
      "web_search": false,
      "status": "ok"
    }
  ],
  "pagination": { "page": 1, "page_count": 20, "total": 311, "total_pages": 16 }
}

cURL

curl "https://nymbot.ai/api/v1/queries/history?page_count=50&type=chat" \
  -H "Authorization: Bearer $NYMBOT_API_KEY"

Python

import os
import requests

res = requests.get(
    "https://nymbot.ai/api/v1/queries/history",
    headers={"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]},
    params={"page_count": 50, "type": "chat"},
)
for row in res.json()["data"]:
    print(row["timestamp"], row["model"], row["cost_sats"])

JavaScript

const res = await fetch("https://nymbot.ai/api/v1/queries/history?page_count=50&type=chat", {
  headers: { "Authorization": "Bearer " + process.env.NYMBOT_API_KEY },
});
for (const row of (await res.json()).data) console.log(row.timestamp, row.model, row.cost_sats);

Account aanvragen ondertekenen

Het maken, wijzigen en herroepen van sleutels, het accountoverzicht en automatische top-ups nemen geen API-sleutel. ze nemen een handtekening van uw nym, zodat een gelekte sleutel tot zijn cap kan besteden, maar nooit een andere sleutel kan maken of zijn eigen cap kan verhogen.

De app doet dit voor u: alles in zijn Vuur Je hebt deze sectie alleen nodig om sleutels uit je eigen code te beheren.

De handtekening is een Nostr-gebeurtenis van soort 27235 (NIP-98), verzonden base64-gecodeerd in de Authorization Header met het woord Nostr Aan de voorzijde:

Het evenement

{
  "kind": 27235,
  "created_at": 1790726400,
  "tags": [
    ["u", "https://nymbot.ai/api/v1/keys"],
    ["method", "POST"],
    ["nonce", "9c4e21f07a3b...16 random bytes in hex"],
    ["payload", "3f1a0d7c8e2b...sha256 of the exact request body in hex"]
  ],
  "content": "",
  "pubkey": "your public key in hex",
  "id": "...",
  "sig": "..."
}
  • u is de volledige URL van de verzoek, query string inbegrepen, precies zoals verzonden.
  • method Dit is de HTTP methode.
  • payload is de SHA-256 van het ruwe verzoek lichaam, in hex. POST En PATCH, en het lichaam dat u verzendt moet byte voor byte zijn dat u hashte.
  • created_at moet binnen 60 seconden van de klok van de server zijn.
  • Elke gebeurtenis werkt één keer, a GET inclusief, zodat een vastgelegde kop kan niet opnieuw worden afgespeeld. Schrijf een nieuwe voor elke aanvraag. Voeg een nonce tag met een willekeurige waarde, zodat twee verzoeken die in dezelfde seconde zijn ondertekend nog steeds verschillen.
  • Het lichaam van een ondertekende verzoek kan maximaal 64 KB zijn, en een lichaam moet Content-Type: application/json.

Een ontbrekende gebeurtenis komt terug 401 missing_nostr_autheen die slecht gevormd, slecht ondertekend, te oud is, of voor een andere URL, methode of lichaam retourneert invalid_nostr_auth, met de reden in de boodschap; een hergebruikt een terugkeert nostr_auth_replayedEen API-sleutel die naar deze eindpunten wordt verzonden, wordt geweigerd.De handtekening wordt gecontroleerd voordat het lichaam wordt gelezen, en elk adres kan het 30 keer per minuut mislukken (een IPv6-adres telt als zijn hele /64); daarna krijgt het 429 met Retry-After.

Browsers kunnen deze eindpunten alleen oproepen vanaf de eigen sites van Nymbot (https://nymbot.ai, https://nymchat.app Een pagina op een andere site krijgt geen CORS-headsets terug, dus het kan niet lezen wat ze teruggeven. OriginZij worden niet beïnvloed.

Uw geheime sleutel

Ondertekening heeft de geheime sleutel van je nym nodig (de nsecAlleen zet het in een script op een machine die je vertrouwt, lees het uit de omgeving in plaats van het in het bestand te schrijven, en de voorkeur de app wanneer je kunt.

Deze helpers bouwen de kop. De latere voorbeelden op deze pagina gebruiken ze. Ze lezen de geheime sleutel in hex uit NOSTR_SECRET_HEX; de cURL een gebruikt de Naak command-line tool, die een nsec of hex-sleutel gebruikt, en sha256sum Voor macOS, shasum -a 256).

cURL

nostr_auth() {
  method="$1"; url="$2"; body="$3"
  nonce=$(openssl rand -hex 16)
  if [ -n "$body" ]; then
    hash=$(printf '%s' "$body" | sha256sum | cut -d' ' -f1)
    event=$(nak event --sec "$NOSTR_SECRET_HEX" -k 27235 -t "u=$url" -t "method=$method" -t "nonce=$nonce" -t "payload=$hash")
  else
    event=$(nak event --sec "$NOSTR_SECRET_HEX" -k 27235 -t "u=$url" -t "method=$method" -t "nonce=$nonce")
  fi
  printf 'Nostr %s' "$(printf '%s' "$event" | base64 | tr -d '\n')"
}

Python

# pip install coincurve requests
import base64, hashlib, json, os, time
from coincurve import PrivateKey, PublicKeyXOnly

SECRET = bytes.fromhex(os.environ["NOSTR_SECRET_HEX"])

def nostr_auth(method, url, body=b""):
    pubkey = PublicKeyXOnly.from_secret(SECRET).format().hex()
    tags = [["u", url], ["method", method], ["nonce", os.urandom(16).hex()]]
    if body:
        tags.append(["payload", hashlib.sha256(body).hexdigest()])
    created_at = int(time.time())
    serialized = json.dumps([0, pubkey, created_at, 27235, tags, ""], separators=(",", ":"), ensure_ascii=False)
    event_id = hashlib.sha256(serialized.encode()).digest()
    event = {
        "id": event_id.hex(),
        "pubkey": pubkey,
        "created_at": created_at,
        "kind": 27235,
        "tags": tags,
        "content": "",
        "sig": PrivateKey(SECRET).sign_schnorr(event_id).hex(),
    }
    return "Nostr " + base64.b64encode(json.dumps(event).encode()).decode()

JavaScript

// npm install nostr-tools
import { createHash, randomBytes } from "node:crypto";
import { finalizeEvent } from "nostr-tools/pure";

const secret = Buffer.from(process.env.NOSTR_SECRET_HEX, "hex");

export function nostrAuth(method, url, body = "") {
  const tags = [["u", url], ["method", method], ["nonce", randomBytes(16).toString("hex")]];
  if (body) tags.push(["payload", createHash("sha256").update(body).digest("hex")]);
  const event = finalizeEvent(
    { kind: 27235, created_at: Math.floor(Date.now() / 1000), tags, content: "" },
    secret,
  );
  return "Nostr " + Buffer.from(JSON.stringify(event)).toString("base64");
}

Samenvatting van de rekening

Wat het API-blad van de app bovenaan laat zien: uw openbare sleutel, beide balans, hoeveel sleutels actief zijn (niet herroepen of verlopen), en de Automatische top-up instellingen, of null Als de server het niet aanbiedt.

GET https://nymbot.ai/api/v1/account nodig a Ondertekend verzoek.

Reactie

{
  "data": {
    "pubkey": "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
    "balances": {
      "standard": { "credits": 120.4, "sats": 1204 },
      "pro": { "credits": 408.33, "sats": 40833 }
    },
    "keys_active": 3,
    "nwc_auto_topup": {
      "connected": true, "threshold_sats": 5000, "topup_sats": 20000, "tier": "pro",
      "last_topup_at": null, "last_topup_sats": null, "last_error": null
    }
  }
}

cURL

URL=https://nymbot.ai/api/v1/account
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"

Python

import requests

url = "https://nymbot.ai/api/v1/account"
print(requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json())

JavaScript

const url = "https://nymbot.ai/api/v1/account";
const res = await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } });
console.log(await res.json());

Beheren van sleutels

De eindpunten achter de sleutellijst van de app. Ze hebben allemaal een Ondertekend verzoekElke sleutel wordt in deze vorm geretourneerd, met tijden in ISO 8601 en bedragen in sats:

Belangrijkste object

{
  "id": "4f0c9a1be27d3856",
  "name": "laptop scripts",
  "hint": "sk-nymbot-Qm7x…c2Lw",
  "limit_sats": 20000,
  "reset_period": "monthly",
  "reset_at": "2026-10-01T00:00:00Z",
  "expire_at": null,
  "period_used_sats": 3412,
  "total_used_sats": 18230,
  "created_at": "2026-08-14T09:21:07Z",
  "updated_at": "2026-09-02T17:40:55Z",
  "last_used_at": "2026-09-30T08:12:00Z",
  "revoked_at": null
}

hint Het is voldoende om een sleutel te herkennen, maar niet om deze te gebruiken.De sleutel zelf wordt slechts één keer teruggegeven, wanneer deze is gemaakt.

Lijst van sleutels

GET https://nymbot.ai/api/v1/keys En ondertekend.

Het veldtypevereistBeschrijving
include_revokedBooleaanse taal (query)nietInclusief ingetrokken sleutels. standaard false.

Reactie

{ "data": [ { "id": "4f0c9a1be27d3856", "name": "laptop scripts", "hint": "sk-nymbot-Qm7x…c2Lw", "...": "..." } ] }

cURL

URL=https://nymbot.ai/api/v1/keys
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"

Python

import requests

url = "https://nymbot.ai/api/v1/keys"
for key in requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json()["data"]:
    print(key["id"], key["name"], key["period_used_sats"], key["limit_sats"])

JavaScript

const url = "https://nymbot.ai/api/v1/keys";
const { data } = await (await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } })).json();
for (const key of data) console.log(key.id, key.name, key.period_used_sats, key.limit_sats);

Een sleutel maken

POST https://nymbot.ai/api/v1/keys Aangekondigd. terug 201.

Het veldtypevereistBeschrijving
nameStringJa1 tot 40 tekens, anders dan uw andere actieve sleutels (geïgnoreerd geval).
limit_satsintegernietDe uitgavencap in sats, ten minste 1.
reset_periodStringnietdaily, weekly of monthlyDe behoeften limit_satsLaat het uit voor een hoes die nooit wordt hersteld.
expire_atString of integernietWanneer de sleutel stopt met werken: een ISO 8601-tijd, of milliseconden sinds 1970.

Beantwoorden (201)

{
  "data": {
    "id": "4f0c9a1be27d3856",
    "name": "laptop scripts",
    "hint": "sk-nymbot-Qm7x…c2Lw",
    "limit_sats": 20000,
    "reset_period": "monthly",
    "key": "sk-nymbot-Qm7x...c2Lw",
    "...": "the rest of the key object"
  }
}
StatusWanneer
400Een ontbrekende of te lange naam; een naam die al in gebruik is (duplicate_name); een cap die niet een geheel getal van ten minste 1 is; een resetperiode zonder cap; een vervaldatum in het verleden; een onbekend veld (unknown_parameter); of 25 actieve sleutels al (too_many_keys).
429Meer dan 60 sleutels gemaakt door deze nimm, of 120 van dit adres, in een uur (rate_limit_exceeded, met Retry-After).

cURL

URL=https://nymbot.ai/api/v1/keys
BODY='{"name":"laptop scripts","limit_sats":20000,"reset_period":"monthly"}'
curl "$URL" \
  -H "Authorization: $(nostr_auth POST "$URL" "$BODY")" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Python

import json
import requests

url = "https://nymbot.ai/api/v1/keys"
body = json.dumps({"name": "laptop scripts", "limit_sats": 20000, "reset_period": "monthly"}).encode()
res = requests.post(
    url,
    data=body,
    headers={"Authorization": nostr_auth("POST", url, body), "Content-Type": "application/json"},
)
print(res.json()["data"]["key"])

JavaScript

const url = "https://nymbot.ai/api/v1/keys";
const body = JSON.stringify({ name: "laptop scripts", limit_sats: 20000, reset_period: "monthly" });
const res = await fetch(url, {
  method: "POST",
  headers: { "Authorization": nostrAuth("POST", url, body), "Content-Type": "application/json" },
  body,
});
console.log((await res.json()).data.key);

Een sleutel lezen

GET https://nymbot.ai/api/v1/keys/{id} En ondertekend.

terugkeren {"data": {…}} met het belangrijkste object, of 404 key_not_found als geen van uw sleutels die id heeft.

cURL

URL=https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"

Python

import requests

url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856"
print(requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json()["data"])

JavaScript

const url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856";
console.log((await (await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } })).json()).data);

Een sleutel wijzigen

PATCH https://nymbot.ai/api/v1/keys/{id} En ondertekend.

Stuur een van de name, limit_sats, reset_period En expire_atmet dezelfde regels als bij het maken van een sleutel. null verwijdert een veld: geen cap, geen reset, geen vervaldatum. Het wijzigen van de resetperiode begint een nieuwe periode bij nul. Een herroepte sleutel kan niet worden gewijzigd (400 key_revoked• Terugkeren {"data": {…}} met het bijgewerkte sleutelobject.

cURL

URL=https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856
BODY='{"limit_sats":50000,"expire_at":null}'
curl -X PATCH "$URL" \
  -H "Authorization: $(nostr_auth PATCH "$URL" "$BODY")" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Python

import json
import requests

url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856"
body = json.dumps({"limit_sats": 50000, "expire_at": None}).encode()
res = requests.patch(
    url,
    data=body,
    headers={"Authorization": nostr_auth("PATCH", url, body), "Content-Type": "application/json"},
)
print(res.json()["data"])

JavaScript

const url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856";
const body = JSON.stringify({ limit_sats: 50000, expire_at: null });
const res = await fetch(url, {
  method: "PATCH",
  headers: { "Authorization": nostrAuth("PATCH", url, body), "Content-Type": "application/json" },
  body,
});
console.log((await res.json()).data);

Een sleutel intrekken

DELETE https://nymbot.ai/api/v1/keys/{id} En ondertekend.

Stopt de sleutel meteen, voor goed. Het blijft in de lijst met revoked_at opgesteld, en kan worden gezien met include_revoked=trueAlleen de nieuwste 50 ingetrokken sleutels worden bewaard; de oudere worden verwijderd wanneer een andere sleutel wordt ingetrokken.

Reactie

{ "data": { "id": "4f0c9a1be27d3856", "revoked": true } }

cURL

URL=https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856
curl -X DELETE "$URL" -H "Authorization: $(nostr_auth DELETE "$URL")"

Python

import requests

url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856"
print(requests.delete(url, headers={"Authorization": nostr_auth("DELETE", url)}).json())

JavaScript

const url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856";
const res = await fetch(url, { method: "DELETE", headers: { "Authorization": nostrAuth("DELETE", url) } });
console.log(await res.json());

Nieuwe NWC Auto-top-up

Sluit een Lightning-portemonnee aan met Nostr Wallet Connect en Nymbot bereikt een evenwicht op zichzelf wanneer API-uitgaven het laag laten lopen. Ondertekend verzoek.

Hoe het werkt: nadat een API-aanvraag wordt gechargeerd op het saldo dat u hebt gekozen om te bekijken, als dat saldo onder uw drempel is gevallen, maakt Nymbot een factuur voor uw top-up bedrag, vraagt uw portemonnee om het te betalen en voegt het krediet toe. Het komt maximaal één keer per 5 minuten voor elke nym en saldo, zodat een uitbarsting van verzoeken de portemonnee niet kan leiden. Uitgaven in de apps triggeren het niet. De tijd en grootte van de laatste top-up, en de laatste fout, zijn in de instellingen; als een betaling na een fout ging, controleer de factuur met De top-up status Credit het wel.

Voordat u een portemonnee aansluit

Nymbot slaat het versleuteld op en gebruikt het alleen om zijn eigen top-up facturen te betalen, maar maak hiervoor een verbinding, met een uitgavenbudget in je portemonnee, dus het meeste dat het ooit zou kunnen betalen is een nummer dat je hebt gekozen. pay_invoice.

Een portemonnee aansluiten

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect En ondertekend.

Het veldtypevereistBeschrijving
nwc_urlStringJaDe verbinding string, beginnend met nostr+walletconnect://Nymbot vraagt de portemonnee voor get_info voorafgaand aan het opslaan, en opslaat het versleuteld.
threshold_satsintegerJaTop up wanneer de balans onder deze vele sats valt.
topup_satsintegerJaHoeveel om elke keer toe te voegen. 1.000 tot 1.000.000 sats.
tierStringnietpro (de default) of standard: de balans om te kijken en top up.

Reactie

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
StatusWanneer
400Geen verbinding met een string (invalid_nwc_url); de portemonnee heeft niet gereageerd op zijn relay (nwc_unreachable) of weigerde de controle (nwc_rejected); de verbinding kan geen facturen betalen (nwc_missing_permission(of een bedrag buiten de limiet.
501Automatische top-ups zijn niet ingeschakeld voor deze server (nwc_unavailableHetzelfde geldt voor de andere twee eindpunten.

cURL

URL=https://nymbot.ai/api/v1/nwc-auto-topup/connect
BODY='{"nwc_url":"nostr+walletconnect://...","threshold_sats":5000,"topup_sats":20000,"tier":"pro"}'
curl "$URL" \
  -H "Authorization: $(nostr_auth POST "$URL" "$BODY")" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Python

import json, os
import requests

url = "https://nymbot.ai/api/v1/nwc-auto-topup/connect"
body = json.dumps({
    "nwc_url": os.environ["NWC_URL"],
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
}).encode()
res = requests.post(
    url,
    data=body,
    headers={"Authorization": nostr_auth("POST", url, body), "Content-Type": "application/json"},
)
print(res.json())

JavaScript

const url = "https://nymbot.ai/api/v1/nwc-auto-topup/connect";
const body = JSON.stringify({
  nwc_url: process.env.NWC_URL,
  threshold_sats: 5000,
  topup_sats: 20000,
  tier: "pro",
});
const res = await fetch(url, {
  method: "POST",
  headers: { "Authorization": nostrAuth("POST", url, body), "Content-Type": "application/json" },
  body,
});
console.log(await res.json());

Lees de instellingen

GET https://nymbot.ai/api/v1/nwc-auto-topup En ondertekend.

Retourneert hetzelfde object als het verbinden, met connected: false En de andere velden null Wanneer er geen portemonnee is aangesloten, wordt de verbindingsreeks zelf nooit teruggestuurd.

cURL

URL=https://nymbot.ai/api/v1/nwc-auto-topup
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"

Python

import requests

url = "https://nymbot.ai/api/v1/nwc-auto-topup"
print(requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json())

JavaScript

const url = "https://nymbot.ai/api/v1/nwc-auto-topup";
console.log(await (await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } })).json());

ontkoppelen

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection En ondertekend.

Verwijdert de opgeslagen verbindingsreeks. Er worden geen meer top-ups gemaakt. Om zeker te zijn, kunt u ook de verbinding in uw portemonnee intrekken.

Reactie

{ "data": { "connected": false, "threshold_sats": null, "topup_sats": null, "tier": null, "last_topup_at": null, "last_topup_sats": null, "last_error": null } }

cURL

URL=https://nymbot.ai/api/v1/nwc-auto-topup/connection
curl -X DELETE "$URL" -H "Authorization: $(nostr_auth DELETE "$URL")"

Python

import requests

url = "https://nymbot.ai/api/v1/nwc-auto-topup/connection"
print(requests.delete(url, headers={"Authorization": nostr_auth("DELETE", url)}).json())

JavaScript

const url = "https://nymbot.ai/api/v1/nwc-auto-topup/connection";
const res = await fetch(url, { method: "DELETE", headers: { "Authorization": nostrAuth("DELETE", url) } });
console.log(await res.json());

Betalen op aanvraag zonder sleutel

De vaste prijs eindpunten kunnen worden betaald voor één verzoek per keer over Lightning, zonder sleutel, geen account en geen saldo: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations En POST /embeddingsChat, Reacties en berichten hebben altijd een sleutel nodig.Een verzoek dat een sleutel draagt, wordt zoals gewoonlijk in rekening gebracht op het saldo; de betalingsstroom begint alleen als er geen sleutel wordt verzonden.

Nymbot spreekt twee versies van hetzelfde idee, van één backend: Lightning Labs' L402 (Ook geaccepteerd onder zijn oude naam, LSAT) en het IETF-ontwerp Betalen HTTP authenticatie schema met de lightning Methode en charge Gebruik wat je klant begrijpt.

Je eigen server runnen

Betalen zonder sleutel is alleen op wanneer API_L402_SECRET bevat ten minste 32 willekeurige bytes, zoals hex (64 tekens) of base64 (44). openssl rand -hex 32Een kortere of vermoedelijke waarde schakelt de functie uit en registreert waarom. API_L402_SECRET_PREVIOUS voor een dag: credentials, status-URL's en uitdagingen die onder het blijven werken tot ze verlopen.

De uitdaging

Stuur het verzoek met nee Authorization header. als het geldig is, loopt niets en je krijgt 402 Payment Required met een factuur voor precies wat dat verzoek kost: dezelfde prijs die een sleutel zou betalen, omgezet op 10 sats een standaard krediet of 100 sats een Pro krediet en afgerond tot een hele sat (ten minste 1 sat, en ten minste de 0.05 credit minimum). WWW-Authenticate Uitdagingen voor dezelfde factuur:

HTTP/1.1 402 Payment Required
Content-Type: application/problem+json; charset=utf-8
Cache-Control: no-store
WWW-Authenticate: L402 macaroon="AgJC...", invoice="lnbc2370n1..."
WWW-Authenticate: LSAT macaroon="AgJC...", invoice="lnbc2370n1..."
WWW-Authenticate: Payment id="kM9x...", realm="nymbot", method="lightning", intent="charge",
    request="eyJhbW91bnQiOiIyMzciLC...", description="Nymbot API POST /images/generations (237 sats)",
    digest="sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:", expires="2026-09-30T12:15:00.000Z",
    opaque="eyJlbmRwb2ludCI6IlBPU1QgL2ltYWdlcy9nZW5lcmF0aW9ucyJ9"

{
  "type": "https://paymentauth.org/problems/payment-required",
  "title": "Payment Required",
  "status": 402,
  "detail": "This request costs 237 sats. Pay the Lightning invoice, then send the identical request again ...",
  "challengeId": "kM9x...",
  "amount_sats": 237,
  "invoice": "lnbc2370n1...",
  "payment_hash": "9db1370f...",
  "expires_at": "2026-09-30T12:15:00.000Z",
  "error": { "message": "This request costs 237 sats. ...", "type": "payment_required", "code": "payment_required", "param": null }
}

De betaling request De volgende parameters zijn base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

Een uitdaging is gebonden aan het eindpunt, aan de Content-Type (het mediatype en, voor multipart, de limiet) en naar de SHA-256 van het exacte lichaam bytes u verzonden, en duurt 15 minuten. identiek Nogmaals gevraagd: hetzelfde Content-Type en dezelfde JSON bytes, of voor de multipart eindpunten (/images/edits, /audio/transcriptions, /audio/translationsDe meeste HTTP-bibliotheken kiezen elke keer dat ze een formulier coderen een nieuwe limiet, dus coderen ze het één keer en sturen die bytes twee keer.

Elk adres kan om 30 uitdagingen per minuut vragen (een IPv6-adres telt als zijn hele /64). Verzoeken waarvan het adres niet bekend is delen een strenger 10 per minuut, en er is een algemene drempel op de uitdagingen Nymbot problemen over alle adressen; een verzoek geweigerd voordat een uitdaging wordt gemaakt (bijvoorbeeld met een lichaam dat niet geldig is JSON) telt niet in de richting ervan. 429 met Retry-AfterBetaalde eindpunten die worden gebeld zonder een sleutel of credential tellen ook in op de algemene limiet van 120 niet-geauthenticeerde verzoeken per minuut per adres. Content-Type is niet application/json (of multipart/form-data voor uploads) wordt geweigerd met 415 En nooit een factuur krijgen.

Embeddings worden geprijsd op basis van een schatting van de tokens in de input, met een margin van 1,5 keer, omdat het werkelijke aantal pas daarna bekend is. Token terugbetalen.

Het verzenden van de betaling

Betaal de factuur met een Lightning portemonnee. De portemonnee geeft u de preimage, 64 hex tekens.

Schemaheader
L402Authorization: L402 <macaroon>:<preimage> (LSAT Het werkt ook)
BetalenAuthorization: Payment <base64url JSON>Waar de JSON is {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

Een betaald verzoek beantwoordt precies zoals een met een sleutel, behalve dat de nymbot object heeft geen balansvelden: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}En er is geen X-Nymbot-Balance-Sats header. Een verzoek dat is betaald met de betalingssysteem krijgt ook een Payment-Receipt header (base64url JSON met de challenge id, de betaling hash als reference, status En timestampBetaalde verzoeken zijn niet gekoppeld aan een nim, dus ze verschijnen niet in de querygeschiedenis.

StatusWanneer
402 payment_already_usedElke betaling betaalt voor één verzoek.Het antwoord is een nieuwe uitdaging voor dit verzoek, dus een klant die zijn laatste credential (zoals lnget Da’s gewoon weer land.
402 payment_mismatchDe credential werd uitgegeven voor een ander eindpunt, Content-Type of lichaam, of betaalt minder dan dit verzoek nu kost. Een nieuwe uitdaging voor dit verzoek komt ermee mee; als de betaling te klein was, komt wat je betaalde terug als een Token terugbetalen (refund_token En refund_sats in het lichaam).
402 payment_expiredMeer dan 15 minuten zijn verstreken sinds de uitdaging. er komt een nieuwe uitdaging mee. Als de voorbeeld toont dat je betaald hebt, komt wat je betaald hebt terug als een Token terugbetalen (refund_token En refund_sats in het lichaam), eenmaal; het credential wordt vervolgens gebruikt.
401 invalid_preimageDe preimage hasht niet op de betalingshash van de factuur.
401 invalid_payment_credentialDe credential is misvormd, is gewijzigd nadat Nymbot het heeft uitgegeven, of namen een betaling hash Nymbot heeft nooit een factuur uitgegeven voor.
429 rate_limit_exceededMeer dan 30 credentials of sleutels die niet konden worden geverifieerd kwamen binnen een minuut van dit adres, of een terugbetaling token werd meer dan 60 keer in een minuut verzonden. Retry-After.

cURL

BODY='{"model":"nano-banana","prompt":"a lighthouse at dusk","response_format":"b64_json"}'
URL=https://nymbot.ai/api/v1/images/generations

# 1. Get the challenge
CH=$(curl -s -D - -o /dev/null "$URL" -H "Content-Type: application/json" -d "$BODY" | grep -i '^www-authenticate: L402')
MAC=$(echo "$CH" | sed -E 's/.*macaroon="([^"]+)".*/\1/')
INVOICE=$(echo "$CH" | sed -E 's/.*invoice="([^"]+)".*/\1/')

# 2. Pay $INVOICE with your wallet and copy the preimage
PREIMAGE=...

# 3. Send the identical request with the credential
curl "$URL" -H "Content-Type: application/json" -H "Authorization: L402 $MAC:$PREIMAGE" -d "$BODY"

lnget

# lnget (Lightning Labs) pays L402 challenges from your own lnd node and retries for you
lnget -X POST -H "Content-Type: application/json" \
  -d '{"model":"nano-banana","prompt":"a lighthouse at dusk","response_format":"b64_json"}' \
  https://nymbot.ai/api/v1/images/generations

Python

import base64, json, re
import requests

url = "https://nymbot.ai/api/v1/images/generations"
body = json.dumps({"model": "nano-banana", "prompt": "a lighthouse at dusk", "response_format": "b64_json"}).encode()
headers = {"Content-Type": "application/json"}

challenge = requests.post(url, data=body, headers=headers)
assert challenge.status_code == 402
info = challenge.json()
print("Pay", info["amount_sats"], "sats:", info["invoice"])

preimage = pay_with_your_wallet(info["invoice"])  # 64 hex characters

# L402
mac = re.search(r'L402 macaroon="([^"]+)"', challenge.headers["WWW-Authenticate"]).group(1)
res = requests.post(url, data=body, headers={**headers, "Authorization": f"L402 {mac}:{preimage}"})
print(res.json()["nymbot"])

JavaScript

const url = "https://nymbot.ai/api/v1/images/generations";
const body = JSON.stringify({ model: "nano-banana", prompt: "a lighthouse at dusk", response_format: "b64_json" });
const headers = { "Content-Type": "application/json" };

const challenge = await fetch(url, { method: "POST", headers, body });
const www = challenge.headers.get("www-authenticate");

// The Payment scheme: echo every challenge parameter back with the preimage
const start = www.indexOf("Payment ");
const params = Object.fromEntries([...www.slice(start + 8).matchAll(/(\w+)="((?:[^"\\]|\\.)*)"/g)].map((m) => [m[1], m[2]]));
const { invoice } = JSON.parse(Buffer.from(params.request, "base64url").toString()).methodDetails;
const preimage = await payWithYourWallet(invoice);

const credential = Buffer.from(JSON.stringify({ challenge: params, payload: { preimage } })).toString("base64url");
const res = await fetch(url, { method: "POST", headers: { ...headers, Authorization: `Payment ${credential}` }, body });
console.log((await res.json()).nymbot, res.headers.get("payment-receipt"));

Klanten zijn gebouwd op mppx met een Lightning-methode omgaan met de Payment-uitdaging zelf; wijzen ze op het eindpunt en laten ze betalen.

Video’s

Een betaald POST /videos Antwoorden 202 als een sleutel één, plus a status_url: GET Het is ondertekend en werkt 24 uur, zolang de taak wordt bewaard.

{
  "id": "vid_...",
  "status": "in_progress",
  "status_url": "https://nymbot.ai/api/v1/videos/vid_...?exp=1790000000&sig=...",
  "nymbot": {
    "payment": "l402", "tier": "pro", "paid_sats": 4800, "charged_sats": 4800,
    "refund_token": "REFUND-5E0B..."
  }
}

Houd de refund_token van dit antwoord: het wordt alleen hier weergegeven. Het is leeg terwijl de video weergeeft (GET /api/v1/l402/refunds Antwoorden "status": "pending"); als de rendering niet onbetaald is, landt de betaling daarop. refund_sats voor een terugbetaalde baan, maar nooit de token, dus het delen van de status-URL deelt de terugbetaling niet.

terugbetalen

Als een betaald verzoek mislukt en de provider Nymbot voor de poging in rekening heeft gebracht, wordt de betaling bewaard en de fout zegt dat, met charged_sats, precies zoals bij een sleutelverzoek. Als het mislukt zonder in rekening te worden gebracht, draagt de fout een Token terugbetalen De moeite waard wat je betaalt:

{
  "error": {
    "message": "The image generator failed. Nothing was charged. Please try again. The 237 sats you paid are on refund token REFUND-...",
    "type": "api_error",
    "code": "upstream_error",
    "refund_token": "REFUND-0C15DBED145D59131BA70298A413ADEB3D9B9AB082C476EA70A5BD38FCA5DE6D",
    "refund_sats": 237,
    "refund_expires_at": "2026-10-30T12:00:00.000Z"
  }
}

Ongebruikte onderdelen komen op dezelfde manier terug: als je twee foto's hebt gevraagd en er een niet is uitgekeerd, is het succes van de reactie nymbot object draagt een terugbetaling token voor de ontbrekende een; een transcriptie waarvan de lengte niet kon worden gelezen vooraf wordt geprijsd voor de langste bestand kan zijn (niet meer dan 30 minuten), en het verschil met de werkelijke lengte komt terug als een terugbetaling token; als het blijkt langer dan 30 minuten, wordt het geweigerd met 413 En de volledige betaling wordt terugbetaald.Inbeddings retourneren wat de schatting vasthield.Een mislukte video wordt terugbetaald naar de token die zijn inzending heeft teruggegeven.

Een restitutietoken is een willekeurige 256-bits code. Nymbot slaat alleen zijn hash op, en het vervalt na 30 dagen.

  • Betalen met het. verzenden Authorization: Bearer REFUND-… Op een van de bovenstaande eindpunten (een OpenAI SDK neemt het als API-sleutel) komt de prijs van het token en blijft er wat overblijft (refund_token_sats In de nymbot Een token die minder waard is dan de vraag beantwoordt 402 refund_insufficientEen onbetaalde mislukking plaatst de sats terug op dezelfde token.
  • Check het maar. GET /api/v1/l402/refunds met dezelfde header terugkeert {"sats": 237, "status": "open", "expires_at": "..."}.
  • Een token kan maximaal 60 keer per minuut worden gebruikt.
  • Verplaats het naar een nym. Pas het in Verkoop een cadeau in de Nymbot-app, of een oproep POST /api/v1/l402/refunds/redeem met a Ondertekend verzoek En {"refund_token": "REFUND-...", "balance": "standard"} (of "pro"Hele credits gaan naar het saldo (10 sats elk op standaard, 100 op Pro); satsen die geen hele credit blijven op de token voor API-verzoeken.

cURL

URL=https://nymbot.ai/api/v1/l402/refunds/redeem
BODY='{"refund_token":"REFUND-...","balance":"standard"}'
curl "$URL" \
  -H "Authorization: $(nostr_auth POST "$URL" "$BODY")" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Reactie

{ "data": { "credited": 23, "tier": "standard", "balance_credits": 123, "remaining_sats": 7 } }