Wiesselt op den Inhalt
Zurück zu Nymbot

Wëssensbasis Entwéckler

Balance, Top-ups a Schlëssel

Kontrolléiert wat Dir hutt, Top up iwwer Lightning, Top up automatesch aus Ärem eegene Portemonnaie, gesinn wat all Fro kostet, an verwalten Schlëssel aus Code.

Kontrolléiert d'Balance

Beide vun Är Balance, an wéi vill vun dësem Schlüssel Kap ass benotzt.

GET https://nymbot.ai/api/v1/credits/balance Dir braucht eng API Schlüssel. POST Et funktionnéiert och, fir Clienten déi dat erwaart.

balance ass déi zwee Saldo zusammen an Dollar am aktuellen Bitcoin Präis, fir Tools déi eng eenzeg Zuel erwaart (null de Präis kann net geléiert ginn). De Rest ass an Credits an Sats, dat ass wéi d'Gewënnlechkeet tatsächlich gehalten gëtt. key De Schlëssel, deen de Cap erreecht huet, kann och de Saldo kontrolléieren.

Reaktioun

{
  "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"
  }
}
Status vumWann
401De Schlëssel ass vermisst, onbekannt, annuléiert oder verëffentlecht.

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);

Payment Method

Wéi kënnt Dir ophalen, an d'Grenzen. Lightning ass déi eenzeg Method.

GET https://nymbot.ai/api/v1/topup/payment-methods Et braucht keng Schlëssel.

E Top-up ass 10 bis 1.000.000 Sats; e Pro-Top-up muss mindestens e Pro-Credit kafen, also et fänkt bei 100 Sats. bulk_bonus listet den Extra-Credit op méi grouss Top-ups, déi selwecht wéi an der App: 10%, 15% oder 20% méi op Standard Top-ups vun 500, 1.000 oder 5.000 Sats, an op Pro Top-ups vun 5.000, 10.000 oder 50.000 Sats.

Reaktioun

{
  "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);

D'Situatioun iwwer Lightning

Et gëtt e Blitz-Rechnung, déi Kredit op d'Nym ass de Schlüssel gehéieren. Kontrolléieren D'Kredit ass dann net ze kréien.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Dir braucht eng API Schlüssel.

FënnefTypVläichtBeschreiwung
amountD’NumérJoWéi vill, an currencyEt ass eng ganz Nummer fir d'Sats.
currencyStrengNieftSATS Duerfir gëtt klenggeschriwwen: USD oder BTCDollars sinn um aktuellen Bitcoin Präis konvertéiert.
tierStrengNieftpro (an der Zäit) oder standard: zu welcher Balance de Kredit gëlt.

A Standard Kredit ass 10 Sats an e Pro Kredit 100 Sats, plus all Bulk Bonus; credits Vläicht heescht dat, wat d'Fraktioune erreecht.

Reaktioun

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
Status vumWann
400Déi éischt Method (unsupported_methodEt gëtt keng sou Adress (unsupported_currencyEt handelt sech ëm zwéi Stären, déi no beienee leien.amount_too_smallAmplaz, datt d'Schounfrist nëmmen ëm ee Mount verlängert gëtt.amount_too_largeEt gëtt kee Gott awer Allah an de Muhammad ass de Messenger vum Allah.amount_out_of_range).
429Méi wéi 60 Rechnungen fir dës nym, oder 120 vun dëser Adress, an enger Stonn (rate_limit_exceededMä mat Retry-After).
502Et gëtt keng Ausféierung (invoice_unavailableMä mat 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);

Kontrolléiert de Top-up

Fragt ob d'Rechnung bezuelt gouf an, wann et huet, huet de Kredit. Checking ass wat ze kréien, also no Bezuelen, kontrolléiert bis de Status ass creditedKontrollen nach ass sécher: d'Kredit landet eng Kéier, egal wéi oft Dir bitt.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - braucht e Schlëssel vun der Nym, déi d'Rechnung gemaach huet.

status Déi pending (Et gëtt keng Ausféierung) paid Et handelt sech ëm zwéi Stären, déi no beienee leien. credited Oder op der Balance. expired Et handelt sech ëm zwéi Stären, déi no beienee leien.

Reaktioun

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
Status vumWann
400D'ID ass net d'64-Charakter-ID vun der Create Call.
404Et geet haut weder Verléierer nach Gewënner.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);

D'Historie gëtt

E Rei fir all Recherche: wat et war, wat Modell, wéi vill Token an wat et kostet. Keine Prompts oder Antworten sinn gehalwe, sou datt keng zréckgeholl ginn. Reen sinn fir 90 Deeg gehalwe, neiste éischt. E Schlüssel, deen seng Kapazitéit erreecht huet, kann nach seng Geschicht liesen.

GET https://nymbot.ai/api/v1/queries/history - braucht eng API Schlëssel, déi seng Properties gesinn, oder eng D'Recherche vum Et ass ee vun de Schlëssel, déi all Schlëssel gesinn.

FënnefTypVläichtBeschreiwung
pageDéi éischt (query)NieftDefault 1, maximal 1000; eng méi héich Säit ass 400 invalid_value.
page_countDéi éischt (query)NieftDefault 20, op maximal 100.
start_date
end_date
D’Qualitéit (Question)NieftISO 8601 Datum oder Zäit.
modelD’Qualitéit (Question)NieftEt ass just dat Modell.
typeD’Qualitéit (Question)Nieftchat, responses, messages, image, video, speech, transcription oder embedding.
all_keysD’Situatioun (Query)NieftBei engem Schlëssel: true Et gëtt all Key vun der selwechter Nym.Default false.
key_idD’Qualitéit (Question)Nieftmat engem ënnerschriwwenen Antrag, oder mat engem all_keys=trueEt ass nëmmen dës Schlëssel.

Reaktioun

{
  "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);

Kontrakt fir Kontoerklärungen

Making, Changing an Revoking Schlëssel, d'Konto Resumé an automatesch Top-ups huelen net eng API Schlëssel. Si huelen eng Signatur vun Ärem Nym, sou datt e leaked Schlëssel kann bis zu senger Kapazitéit bezuelen, mä kann nie eng aner Schlëssel maachen oder seng eegene Kapazitéit erhéijen.

D'App mécht dat fir Iech: alles an senger Feuer gëtt Dir braucht nëmmen dës Sektioun fir Schlëssel aus Ärem eegene Code ze verwalten.

D'Signatur ass e Nostr Event vun der Typ 27235 (NIP-98), geschéckt base64-kodéiert an der Authorization Header mat dem Wort Nostr An der Front:

Evenementer

{
  "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 ass déi vollstänneg URL vun der Recherche, query string inkludéiert, genau wéi geschéckt.
  • method Et ass déi HTTP Method.
  • payload ass de SHA-256 vun der Raw Request Body, an Hex. POST an PATCHFirwat ee steet, a wéi eng Visioun ee fir Lëtzebuerg hätt.
  • created_at Et muss innerhalb vun 60 Sekonnen vun der Server-Uhr sinn.
  • All Event funktionéiert eng Kéier, a GET Et ass eng vun de wichtegsten Punkten fir d'Kritik an d'Kritik an d'Kritik an d'Kritik. nonce Tag mat engem randomiséierter Wäert, sou datt zwee Anfragen, déi an der selwechter Sekonde ënnerschriwwen sinn, nach differenzéiert sinn.
  • De Kierper vun enger ënnerschriwwener Recherche kann maximal 64 KB sinn, an e Kierper braucht Content-Type: application/json.

Et gëtt nach eng fehlende Event. 401 missing_nostr_auth; ee, deen schlecht geformt ass, schlecht signéiert, zu alt, oder fir eng aner URL, Method oder Kierper Returns invalid_nostr_authEt handelt sech ëm zwéi Stären, déi no beienee leien. nostr_auth_replayedEng API-Schlüssel, déi an dës Endpunkten geschéckt gëtt, gëtt refuséiert. D'Signatur gëtt kontrolléiert, ier de Kierper geléiert gëtt, an all Adress kann et 30 Mal pro Minute falen (e IPv6 Adress zählt als seng ganzer /64); no deem gëtt et 429 mat Retry-After.

De Browser kann dës Endpunkter nëmmen vun den eegenen Websäiten vum Nymbot (https://nymbot.ai, https://nymchat.app Eng Säit op all aner Site kritt keng CORS Header zréck, sou datt et net kann liesen wat se zréckbréngen. OriginSi sinn net betroffen.

De Schlëssel ass

Fir ze signéieren braucht Dir de geheimen Schlüssel vun Ärem Nym (de nsec), déi alles kontrolléiert: Är Identitéit, Är Geschicht a Är Balance. nëmmen et an engem Skript op enger Maschinn, déi Dir vertraut, liest et aus der Ëmwelt anstatt et an der Datei ze schreiwen, an d'App wann Dir kann.

Dës Helfer bauen den Header. D'spéider Beispiele op dëser Säit benotzen se. Si liesen de geheime Schlüssel am Hex aus NOSTR_SECRET_HEXD'CURL een benotzt déi D’Nak Kommandozeilen-Tool, dat e nsec oder hex Schlëssel nimmt, an sha256sum Op der Säit 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");
}

Konto Resumé

Wat d'API-Archiv vun der App an der oberer Säit ze weisen: Är öffentlechen Schlëssel, Beide Balances, wéi vill Schlëssel aktiv sinn (kann net annulliert oder verëffentlecht ginn) a Automatesch Top-up De Setup oder null Wann de Server net ze bidden.

GET https://nymbot.ai/api/v1/account Dir braucht a D'Recherche vum.

Reaktioun

{
  "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());

Gestioun vun de Schlëssel

D'Endpunkten déi ënner der Schlësselliste vun der App sinn. All si brauchen eng D'Recherche vumAll Schlëssel gëtt an dëser Form returnéiert, mat Times an ISO 8601 an Betriber an sats:

Déi wichtegst Objet

{
  "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 Et ass genuch fir e Schlëssel ze erkennen, awer net ze benotzen.

Schlëssel an Schlëssel

GET https://nymbot.ai/api/v1/keys Et ass signéiert.

FënnefTypVläichtBeschreiwung
include_revokedD’Situatioun (Query)NieftD'Käschte fir d'Käschte fir d'Käschte fir d'Käschte. false.

Reaktioun

{ "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);

Et gëtt en Schlüssel

POST https://nymbot.ai/api/v1/keys Et ass fir d’Retour. 201.

FënnefTypVläichtBeschreiwung
nameStrengJo1 bis 40 Zeichen, anders wéi Är aner aktiv Schlëssel ( ignoréiert Fall).
limit_satsIntegratiounNieftD'Ausgabenkap op Satz, op d'mannst 1.
reset_periodStrengNieftdaily, weekly oder monthlyBedürfnis limit_satsEt gëtt eng Kapazitéit, déi nie reset.
expire_atStreng oder integerNieftWann de Schlëssel funktionnéiert: eng ISO 8601 Zäit, oder Millisekonnen vu 1970.

Déi éischt (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"
  }
}
Status vumWann
400Eng missend oder zu laang Numm; e Numm deen och am Gebrauch ass (duplicate_name); eng Cap, déi net eng ganze Zuel vun mindestens 1 ass; eng Resetperiod ouni Cap; eng Ausféierung an der Vergangenheit; en onbekannte Feld (unknown_parameter); oder 25 aktive Schlëssel schon (too_many_keys).
429Méi wéi 60 Schlëssel vun dëser nym, oder 120 vun dëser Adress, an enger Stonn gemaach (rate_limit_exceededMä mat 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);

Leeschtung vun engem Schlüssel

GET https://nymbot.ai/api/v1/keys/{id} Et ass signéiert.

Retournéieren {"data": {…}} mat dem Schlësselobjekt, oder 404 key_not_found Wann Dir dës Identitéit hutt.

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);

Ännerungen vum Schlëssel

PATCH https://nymbot.ai/api/v1/keys/{id} Et ass signéiert.

Schreift e puer name, limit_sats, reset_period an expire_atEt ass déi selwecht Regelen wéi bei der Schlësselung. null Et gëtt keng Reset, keng Reset, keng Expiratioun. Ännerung vun der Reset Period huet eng nei Period op null ugefaangen.400 key_revokedD’Retour {"data": {…}} Et gëtt den aktualiséierte Key Object.

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);

Et gëtt eng Schlëssel

DELETE https://nymbot.ai/api/v1/keys/{id} Et ass signéiert.

Et bleift op der Lëscht mat de Schlëssel, fir béid. revoked_at Et gëtt och gesot, mee kann include_revoked=trueD'Revolutioun vun engem Schlëssel, deen schon revokéiert ass, reagéiert op déi selwecht Art. Nur déi neisten 50 revokéiert Schlëssel sinn gehalten; déi aler Schlëssel ginn geléist wann eng aner Schlëssel revokéiert gëtt.

Reaktioun

{ "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());

NWC Auto-Top-up ass eng vun de wichtegsten

Verbannen e Lightning-Wallet mat Nostr Wallet Connect an de Nymbot huet e Balance vu seegen, wann d'API-Ausgaben et niddreg lafen. D'Recherche vum.

Wéi et funktionnéiert: no enger API-Befruchtung op de Saldo opgeléist ass, wann dës Saldo ënner Är Schwéierpunkt gefall huet, féiert Nymbot eng Rechnung fir Är Top-up Betrag, bitt Är Portemonnaie fir se ze bezuelen, an féiert d'Kredit. Et toppt op maximal eng Kéier all 5 Minutten fir all Nym a Saldo, sou datt eng Ausbrechung vun Forderungen d'Portemonnaie net drénken kann. Ausgaben an den Apps huet et net ausgeléist. D'Zäit an d'Gréisst vun der letzter Top-up, an der letzter Fehler, sinn an de Einstellungen; wann eng Bezuelung nach engem Fehler geschitt, kontrolléiert seng Rechnung mat De Top-up Status Kreditt ze kréien.

Wann Dir e Portemonnaie verbënnt

Eng Verbindungszeile erlaabt all déi et hält Är Portemonnaie ze bezuelen. Nymbot späichert et verschlüsselt an nëmmen jemols benotzt et fir seng eegene Top-up Rechnungen ze bezuelen, awer eng Verbindung just fir dat ze maachen, mat engem Ausgabenbudget an Ärem Portemonnaie, sou datt de meeschte kann et jemols bezuelen ass eng Nummer wäerte Dir. pay_invoice.

Et ass eng Portemonnaie

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect Et ass signéiert.

FënnefTypVläichtBeschreiwung
nwc_urlStrengJoD'Connectivitéit beginn an nostr+walletconnect://Nymbot bitt de Portemonnaie fir get_info Et handelt sech ëm zwéi Stären, déi no beienee leien.
threshold_satsIntegratiounJoD'Bilanz fällt ënner déi villen Sätze.
topup_satsIntegratiounJoWat fir eng Kéier ze maachen? 1000 bis 1.000.000 Sätze.
tierStrengNieftpro (an der Zäit) oder standard: d'Balance ze beobachten an top up.

Reaktioun

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
Status vumWann
400Et gëtt keng Verbindung (invalid_nwc_urlEt geet haut weder Verléierer nach Gewënner.nwc_unreachableEt gëtt keng sou Adress (nwc_rejectedEt handelt sech ëm zwéi Stären, déi no beienee leien.nwc_missing_permission) oder e Betrag ausserhalb vun de Grenzen.
501D'Automatik Top-ups sinn net fir dës Server aktivéiert (nwc_unavailableDat gleeft och op déi aner zwee Endpunkten.

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());

Liest déi Settings

GET https://nymbot.ai/api/v1/nwc-auto-topup Et ass signéiert.

Retournéiert déi selwecht Objet wéi verbannen, mat connected: false Déi aner Felder null Wann eng Portemonnaie verbunden ass, gëtt d'Verbindungskette selbst nie zurückgegeben.

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());

Disconnectioun

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection Et ass signéiert.

D'Beschlësselung vun der Verbindung ass ganz einfach. Dir kënnt d'Verbindung an de Portemonnaie annuléieren.

Reaktioun

{ "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());

Bezuelung op Bestellung ouni Schlëssel

D'Fixed-Price Endpoints kënnen fir eng Recherche an enger Zäit iwwer Lightning bezeechent ginn, ouni Schlëssel, keng Konto an keng Saldo: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations an POST /embeddingsDe Chat, d'Responsen an d'Messagen brauchen ëmmer e Schlëssel. eng Recherche, déi e Schlëssel trägt, gëtt als normal op de Saldo fakturéiert; de Bezuelungsfluss fänkt nëmmen wann keng Schlëssel geschéckt gëtt.

Nymbot spricht zwee Versiounen vun der selwechter Idee, aus engem Backend: Lightning Labs' Mäerz 402 (Et gëtt och ënner dem Numm genannt, LSAT) an de IETF Projet Bezuelen HTTP-Authentifizéierungsschema mat der lightning Method an charge Et ass einfach ze benotzen, wat de Client versteet.

Dir kënnt Äre eegene Server

Bezuelung ouni Schlëssel ass nëmmen op wann API_L402_SECRET huet mindestens 32 zufälleg Byte, wéi hex (64 Zeichen) oder base64 (44). openssl rand -hex 32Eng kortere oder erwaartbar Wäert schaltet d'Funktioun aus an logt firwat. API_L402_SECRET_PREVIOUS fir e Dag: Credentials, Status-URLs an Herausforderungen, déi ënner him gemaach sinn, bleiwen net funktionnéieren bis se verlaangen.

Herausforderung

D'Recherche mat No geschéckt Authorization Wann et gültig ass, funktionnéiert nichts, an Dir kënnt 402 Payment Required mat enger Rechnung fir genau wat déi Recherche kostet: de selwechte Präis, deen e Schlëssel zahlen würde, bei 10 Sats a Standardkredit oder 100 Sats a Pro-Kredit konvertéiert an bis zu engem ganzen Sats arrondéiert (minimum 1 Sats, an mindestens den 0,05 Kreditminimum). WWW-Authenticate Problemer fir déi selwecht Rechnung:

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 }
}

Auszahlung request De Parameter ass base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

Et ass eng Herausforderung am Endpunkt, op der Content-Type (d'Mediatyp an, fir Multipart, d'Grenz) an de SHA-256 vun de exakte Kierper Byte Dir geschéckt, an dauert 15 Minutten. Identitéit Déi éischt Recherche: Ähnlech Content-Type Et handelt sech ëm zwéi Stären, déi no beienee leien./images/edits, /audio/transcriptions, /audio/translationsDéi meescht HTTP Bibliothéiken wählen eng nei Grenze allgemeng wann se e Formulär codéieren, also encode et eng Kéier an send déi Byte zweet.

All Adress kann fir 30 Herausforderungen pro Minutt fragen (e IPv6 Adress zählt als seng ganzer /64). Requests mat enger Adress, déi net bekannt ass, deelen eng strenger 10 pro Minutt, an et ass eng allgemeng Kapazitéit op de Herausforderungen Nymbot Problemer iwwer all Adressen; eng Requête verweigert, ier eng Herausforderung gemaach gëtt (zB mat engem Kierper, deen net valid JSON) z'erreechen. 429 mat Retry-AfterD'bezuelten Endpunkten, déi ouni Schlëssel oder Credential genannt ginn, zählen och zu der allgemenger Grenze vun 120 unauthenticated Requests pro Minute pro Adress. Content-Type Et gëtt application/json oder multipart/form-data Mir entschëllegen eis fir de Mëssel.— 415 Et geet haut weder Verléierer nach Gewënner.

Embeddings sinn aus enger Schätzung vun den Token am Input, mat engem 1,5× Margin, well d'echte Zuel nëmmen nach bekannt ass. Rendez-vous vum Token.

Ausféierung vun der Bezuelung

Pay the invoice with any Lightning wallet. The wallet gives you the preimage, 64 hex characters. Da send the same request with one of these:

SchrëttHeescht
Mäerz 402Authorization: L402 <macaroon>:<preimage> (LSAT Et funktionnéiert och)
BezuelenAuthorization: Payment <base64url JSON>Wann de JSON {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

Eng bezuelten Anfrage beantwortet genau wéi eng mat engem Schlëssel gemaach, ausser dass de nymbot Objet huet keng Balance Felder: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}Et gëtt no X-Nymbot-Balance-Sats Header. Eng Recherche déi mat der Payment Scheme bezeechent gëtt och eng Payment-Receipt header (base64url JSON mat der Challenge id, de Payment Hash als reference, status an timestampPayed Requests sinn net zu all nym gebunden, sou datt se an der Quergeschicht net erscheinen.

Status vumWann
402 payment_already_usedAll Bezuelen bezuelt fir eng Recherche. d'Reaktioun ass eng nei Herausforderung fir dës Recherche, also e Client, deen seng lescht Credential cache (wéi lnget Et ass einfach einfach ze kommen.
402 payment_mismatchD'Kreditiatioun ass fir en anere Endpunkt ausgegebt, Content-Type oder Kierper, oder bezuelt manner wéi dës Recherche elo kostet. eng nei Herausforderung fir dës Recherche ass mat et; wann d'Zahlung zu kleng war, wat Dir bezuelt gëtt als eng Rendez-vous vum Token (refund_token an refund_sats an de Kierper)
402 payment_expiredMéi wéi 15 Minutten vun der Herausforderung. eng fräie Herausforderung ass mat et. Wann d'Preview weist datt Dir bezuelt hutt, wat Dir bezuelt huet, kënnt als e Rendez-vous vum Token (refund_token an refund_sats Firwat ee steet, a wéi eng Visioun ee fir Lëtzebuerg hätt.
401 invalid_preimageD'Preimage hasht net op d'Payment-Hash vun der Rechnung.
401 invalid_payment_credentialDe Credential ass falsch geformt, gouf geännert no Nymbot huet et ausgezeechent, oder Nymbot huet nie eng Rechnung fir e Payment Hash genannt.
429 rate_limit_exceededMéi wéi 30 Credentials oder Schlëssel, déi net verifizéiert hunn, kommen aus dëser Adress an enger Minute, oder e Remboursement Token gouf méi wéi 60 Zeiten an enger Minute geschéckt. 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"));

Clienten baséieren op mppx mat enger Lightning Method handelen d'Payment Challenge selwecht; ze weisen an de Endpunkt an liicht se bezuelen.

Videoen

Payéiert POST /videos Antworten 202 Et ass wéi e Schlëssel, a status_url: GET Et ass ënnerschriwwen a funktionnéiert fir 24 Stonnen, solange de Job gehalten gëtt.

{
  "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..."
  }
}

Gehalen déi refund_token D'Bildung vun der Video-Rendez-vous ass op der Video-Rendez-vous (GET /api/v1/l402/refunds Antworten "status": "pending"Wann d'Renderung unfault, ass d'Zahlung op et gelangt. refund_sats fir e refundéiert Job, awer nie de Token, also deelen d'Status URL deelt net de Remboursement.

Refinanzéierung

Wann e bezuelten Erënnerung feelt an de Provider Nymbot fir de Versuch berechnet huet, gëtt d'Zahlung gehalten an de Fehler sagt dat, mat charged_satsWann et net funktionnéiert, wéi wann et net funktionnéiert, dann ass et eng Fehler. Rendez-vous vum Token Wat Dir bezuelt hutt:

{
  "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"
  }
}

Obenutzte Parten kommen op déi selwechte Wee: wann Dir fir zwee Fotoen gefrot hutt an e fëllt net unberechnet, de Erfolleg vun der Reaktioun nymbot Objet huet e Remboursement Token fir de missende; eng Transkriptioun, déi d'Längt vun der fréiere kann net opgeléist ginn, ass fir déi längst d'Datei kann sinn (nicht méi wéi 30 Minutten), an d'Differenz fir d'echte Längt kënnt als Remboursement Token; wann et méi wéi 30 Minutten ass, ass et verweigert. 413 D'Integratioun féiert d'Behandlung an d'Behandlung an d'Behandlung an d'Behandlung an d'Behandlung an d'Behandlung an d'Behandlung an d'Behandlung.

En Remboursement Token ass e zufälligen 256-Bit Code. Nymbot späichert nëmmen seng Hash, an et verfällt nach 30 Deeg. Et hält e sat Balance, an Dir kënnt:

  • Payéiert se mat. Schrëtt Authorization: Bearer REFUND-… Et handelt sech ëm zwéi Stären, déi no beienee leien.refund_token_sats an der nymbot Objet). E Token ass manner wéi d'Antworten vun der Recherche. 402 refund_insufficientEt ass eng vun de wichtegsten Punkten fir d'Realisatioun vun der selwechter Betrachtung.
  • Check it op. GET /api/v1/l402/refunds Déi selwecht Header Returns {"sats": 237, "status": "open", "expires_at": "..."}.
  • Ee Token kann maximal 60 Mal pro Minutt benotzt ginn.
  • Et ass an engem Nym. Passe it op Et gëtt e Geschenk an der Nymbot App, oder Call POST /api/v1/l402/refunds/redeem Wéi a D'Recherche vum an {"refund_token": "REFUND-...", "balance": "standard"} oder "pro"All Credits gi op de Saldo (10 Sats all op Standard, 100 op Pro); Sats déi net eng ganzer Kredit op de Token fir API-Befroen maachen.

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"

Reaktioun

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