Przejdź do treści
Wróć do Nymbot

Baza wiedzy deweloperzy

Równowaga, top-ups i klucze

Sprawdź, co masz, przejdź przez Lightning, przejdź automatycznie z własnego portfela, zobacz, ile kosztuje każde żądanie i zarządzaj kluczami z kodu.

Sprawdź bilans

Zarówno twoje bilansy, jak i ile kapsułki tego klucza jest używane.

GET https://nymbot.ai/api/v1/credits/balance Potrzebny klucz API. POST Działa również dla klientów, którzy tego oczekują.

balance jest dwa salda razem w dolarach w obecnej cenie Bitcoin, dla narzędzi, które oczekują pojedynczej liczby (null Reszta jest w kredytach i ratach, co jest sposobem, w jaki saldo jest faktycznie przechowywane. key Klucz, który osiągnął limit, nadal może sprawdzić saldo.

Odpowiedź

{
  "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"
  }
}
StatusKiedy
401Klucz jest brakujący, nieznany, odwołany lub wygasły.

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

Metody płatności

Jak możesz się podnieść, a granice. błyskawica jest jedyną metodą.

GET https://nymbot.ai/api/v1/topup/payment-methods Nie potrzebny klucz.

Top-up to od 10 do 1 000 000 sats; Pro top-up musi kupić co najmniej jeden kredyt Pro, więc zaczyna się od 100 sats. bulk_bonus wykazuje dodatkowy kredyt na większych top-upach, taki sam jak w aplikacji: 10%, 15% lub 20% więcej na standardowych top-upach od 500, 1000 lub 5000 sats, a na top-upach Pro od 5000, 10.000 lub 50 000 sats.

Odpowiedź

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

W górę nad błyskawicą

Tworzy fakturę Lightning, która dodaje kredyt do nym klucz należy do. Zapłać go z dowolnego portfela Lightning, a następnie Sprawdź to Aby uzyskać dodatkowy kredyt.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Potrzebny klucz API.

polutypuWymaganeOpis
amountLiczbaTakIle, w currencyCała liczba dla stawki.
currencyStringówNieSATS w przypadku deficytu, USD lub BTCDolar konwertuje się po bieżącej cenie Bitcoina.
tierStringówNiepro (w przypadku deficytu) lub standard• do jakiego celu przeznaczony jest kredyt.

Standardowy kredyt to 10 sats i kredyt Pro 100 sats, plus wszelkie bonusy zbiorcze; credits Powiedz, co ta książka doda.

Odpowiedź

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
StatusKiedy
400Inne metody na drodze (unsupported_method), nieznana waluta (unsupported_currency) lub poziom, brakująca kwota lub kwota poniżej minimum (amount_too_small) powyżej 1 000 000 stóp (amount_too_large) lub odrzucony przez portfel błyskowy (amount_out_of_range).
429Ponad 60 faktur za ten nym, lub 120 z tego adresu, w ciągu godziny (rate_limit_exceeded, z Retry-After).
502W tym momencie nie można wykonać żadnej czynności (invoice_unavailable, z 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);

Sprawdź top-up

Pyta, czy faktura została zapłacona i, gdy już jest, dodaje kredyt. Sprawdzanie to, co ją kredytuje, więc po zapłaceniu, sprawdź, aż stan jest creditedSprawdzanie ponownie później jest bezpieczne: kredyt ląduje raz, niezależnie od tego, ile razy go poprosisz.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - potrzebuje klucza od nym, który dokonał faktury.

status Jest pending (na razie nie wypłacono żadnych opłat) paid (Płacone, ale jeszcze nie przyznane; sprawdź ponownie), credited (na twojej równowadze) lub expired (nie zapłacone na czas) Pole bilansu są dla poziomu, na którym faktura sięga.

Odpowiedź

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
StatusKiedy
400Identyfikator nie jest 64-znakowym identyfikatorem połączenia utworzonego.
404Nie ma faktury przez to id dla twojego 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);

Chce historii

Jeden wiersz na żądanie: co to było, jaki model, ile tokenów i ile kosztuje. Nie przechowywane są żadne polecenia ani odpowiedzi, więc żadne nie są zwracane. Linie są przechowywane przez 90 dni, najnowsze najpierw.

GET https://nymbot.ai/api/v1/queries/history — potrzebuje klucza API, który widzi własne żądania, lub Podpisany wniosek z twojego nym, który widzi każdy klucz.

polutypuWymaganeOpis
pageIntegracja z żądaniem (query)NieDomyślny 1, maksymalnie 1000; wyższa strona jest 400 invalid_value.
page_countIntegracja z żądaniem (query)NieRodzaje na stronę. domyślne 20, maksymalnie 100.
start_date
end_date
String (wniosek o pomoc)NieISO 8601 daty i godziny.
modelString (wniosek o pomoc)NieTylko ten model.
typeString (wniosek o pomoc)Niechat, responses, messages, image, video, speech, transcription lub embedding.
all_keysBoolean (wniosek o pracę)NieZ jednym kluczem: true zawiera każdy klucz tego samego nym. Default false.
key_idString (wniosek o pomoc)Niez podpisaniem wniosku lub z all_keys=trueTylko ten klucz.

Odpowiedź

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

Podpisanie wniosków o konto

Tworzenie, zmiana i cofnięcie kluczy, podsumowanie konta i automatyczne top-upy nie biorą klucza API.Bierą podpis od twojego nym, więc wyciekły klucz może wydać do swojej kapsuły, ale nigdy nie może zrobić innego klucza lub podnieść własnej kapsuły.

Aplikacja robi to za Ciebie: wszystko w swoim zakresie Ogień Ta sekcja jest potrzebna tylko do zarządzania kluczami z własnego kodu.

Podpis jest wydarzeniem Nostr typu 27235 (NIP-98), wysłanym z kodowaniem base64 w Authorization Header ze słowem Nostr Na froncie :

Wydarzenie

{
  "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 jest pełny adres URL żądania, łańcuch zapytania wliczony, dokładnie tak, jak wysłano.
  • method Jest to metoda HTTP.
  • payload jest SHA-256 surowego ciała żądania, w hex. POST I PATCH, a ciało, które wysyłasz, musi być bytem za bytem tego, co hashed.
  • created_at musi być w ciągu 60 sekund czasu serwera.
  • Każde wydarzenie działa raz, a GET nie można odtworzyć nagłówka, który został przechwycony. Podpisz nowy nagłówek na każde żądanie. nonce tag z wartością losową, aby dwie prośby podpisane w tej samej sekundzie nadal się różniły.
  • Korpus podpisanego żądania może wynosić maksymalnie 64 KB, a korpus wymaga Content-Type: application/json.

Brakujące wydarzenie powraca 401 missing_nostr_auth, który jest źle utworzony, źle podpisany, zbyt stary lub dla innego adresu URL, metody lub ciała zwraca invalid_nostr_auth, z powodem w wiadomości; używany jeden powraca nostr_auth_replayedPodpis jest sprawdzany, zanim ciało zostanie odczytane, a każdy adres może go uszkodzić 30 razy na minutę (adres IPv6 liczy się jako całość /64); po tym czasie otrzymuje 429 z Retry-After.

Przeglądarki mogą wywołać te punkty końcowe tylko z własnych stron Nymbotu (https://nymbot.ai, https://nymchat.app Strona na dowolnej innej stronie nie otrzymuje nagłówków CORS, więc nie może odczytać tego, co zwracają. OriginNie są one dotknięte.

Twój sekretny klucz

Podpisanie potrzebuje tajnego klucza twojego nyma (tzw. nsec), który kontroluje wszystko: twoją tożsamość, historię i saldo. Po prostu umieść go w skrypcie na maszynie, której ufasz, przeczytaj go z otoczenia zamiast pisać go do pliku i preferuj aplikację, gdy możesz.

Pomocnicy ci budują nagłówek. Późniejsze przykłady na tej stronie używają ich. Czytają tajny klucz w sześciokątnej skrócie od NOSTR_SECRET_HEX; cURL jeden używa Nak narzędzie wiersza poleceń, które bierze klucz nsec lub hex, i sha256sum W przypadku 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");
}

Podsumowanie rachunku

Co na liście API aplikacji jest wyświetlane na górze: klucz publiczny, oba bilansy, ile kluczy jest aktywnych (nie odwołanych lub wygasłych) oraz Automatyczny top-up ustawień lub null Serwer nie oferuje ich.

GET https://nymbot.ai/api/v1/account Potrzebujemy a Podpisany wniosek.

Odpowiedź

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

Zarządzanie kluczami

Punkty końcowe za listą kluczy aplikacji. Wszystkie z nich wymagają Podpisany wniosekKażdy klucz jest zwracany w następującym formularzu, z czasem w ISO 8601 i kwotami w sats:

Kluczowy obiekt

{
  "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 Wystarczy rozpoznać klucz, ale nie użyć go, a sam klucz jest zwracany tylko raz.

Lista kluczy

GET https://nymbot.ai/api/v1/keys – Podpisałem się

polutypuWymaganeOpis
include_revokedBoolean (wniosek o pracę)NieZawiera odwołane klucze. domyślne false.

Odpowiedź

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

Tworzenie klucza

POST https://nymbot.ai/api/v1/keys Podpisane - powracające 201.

polutypuWymaganeOpis
nameStringówTak1 do 40 znaków, różniących się od innych aktywnych kluczy (ignorowanie przypadku).
limit_satsintegerNieKapitał wydatków w sats, co najmniej 1. pozostaw go bez kapitału.
reset_periodStringówNiedaily, weekly lub monthlyPotrzeby limit_satsZostaw go na pokrywę, która nigdy się nie resetuje.
expire_atString lub integerNieKiedy klucz przestaje działać: czas ISO 8601 lub milisekundy od 1970 roku.

Odpowiedź (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"
  }
}
StatusKiedy
400Brakujące lub zbyt długie imię; nazwisko już używane (duplicate_name); kapitał, który nie jest liczbą całkowitą co najmniej 1; okres resetowania bez kapitału; wygaśnięcie w przeszłości; pole nieznane (unknown_parameter); lub 25 aktywnych kluczy już (too_many_keys).
429Ponad 60 kluczy wykonanych przez ten nym, lub 120 z tego adresu, w ciągu godziny (rate_limit_exceeded, z 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);

Czytanie jednego klucza

GET https://nymbot.ai/api/v1/keys/{id} – Podpisałem się

Powrót {"data": {…}} z kluczowym obiektem, lub 404 key_not_found jeśli żaden z kluczy nie ma tego identyfikatora.

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

Zmiana klucza

PATCH https://nymbot.ai/api/v1/keys/{id} – Podpisałem się

Wyślij dowolną name, limit_sats, reset_period I expire_atz tymi samymi zasadami, co przy tworzeniu klucza. null Wyczyści pole: bez limitu, bez resetu, bez wygaśnięcia. Zmiana okresu resetu rozpoczyna nowy okres od zera. Odwołanego klucza nie można zmienić (400 key_revoked) powrót {"data": {…}} z zaktualizowanym obiektem klucza.

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

Odwołanie klucza

DELETE https://nymbot.ai/api/v1/keys/{id} – Podpisałem się

Zatrzymuje klucz od razu, na dobre. Pozostaje na liście z revoked_at Umieszczone i można je zobaczyć include_revoked=trueOdwołanie klucza, który został już odwołany odpowiada w ten sam sposób. Tylko najnowsze 50 odwołanych kluczy są zachowywane; starsze są usuwane, gdy odwołany jest inny klucz.

Odpowiedź

{ "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 na żywo

Podłącz portfel Lightning z Nostr Wallet Connect, a Nymbot samodzielnie podnosi bilans, gdy wydatki API są niskie. arkusz API aplikacji ma te same ustawienia; są to końcowe punkty za nim. Podpisany wniosek.

Jak to działa: po żądanie API jest pobierane do salda, który zdecydowałeś się oglądać, jeśli saldo spadło poniżej progu, Nymbot wystawia fakturę na twoją kwotę top-up, prosi twój portfel, aby go zapłacić, i dodaje kredyt. Występuje maksymalnie raz na 5 minut dla każdego nym i salda, więc wybuch żądań nie może opróżnić portfela. Wydatki w aplikacjach nie wywołują go. Czas i wielkość ostatniego top-up, a ostatni błąd, są w ustawieniach; jeśli płatność przeszła po błędzie, sprawdź jego fakturę z Status top-upowy kredytów na to.

Zanim podłączysz portfel

Nymbot przechowuje go zaszyfrowany i używa go tylko do płacenia własnych faktur, ale połączenie tylko dla tego, z budżetem wydatków w portfelu, więc najbardziej może kiedykolwiek zapłacić jest numer wybrany przez Ciebie. portfel musi obsługiwać pay_invoice.

Podłączanie portfela

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect – Podpisałem się

polutypuWymaganeOpis
nwc_urlStringówTakŹródło połączenia, początek nostr+walletconnect://Nymbot prosi o portfel get_info zanim go zapiszesz, i przechowuje go zaszyfrowany.
threshold_satsintegerTakNajwyżej, gdy saldo spadnie poniżej tych wielu stawek.
topup_satsintegerTakIle można dodać za każdym razem. od 1000 do 1000 000 sats.
tierStringówNiepro (w przypadku deficytu) lub standardRównowaga do oglądania i top up.

Odpowiedź

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
StatusKiedy
400Nie jest to połączenie sieciowe (invalid_nwc_url); portfel nie odpowiedział na jego relay (nwc_unreachable) lub odrzucił kontrolę (nwc_rejected); połączenie nie może płacić faktur (nwc_missing_permissiona) kwotę, która wykracza poza granice.
501Automatyczne top-upy nie są włączone dla tego serwera (nwc_unavailableTo samo dotyczy dwóch pozostałych punktów końcowych.

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

Czytanie ustawień

GET https://nymbot.ai/api/v1/nwc-auto-topup – Podpisałem się

Zwraca ten sam obiekt, który łączy, z connected: false oraz pozostałe pola null gdy portfel nie jest podłączony. łańcuch połączenia nigdy nie jest zwracany.

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

Odłączanie

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection – Podpisałem się

Usuwa zapisany łańcuch połączeń. Nie tworzy się więcej top-upów. Aby być pewnym, możesz również wycofać połączenie w portfelu.

Odpowiedź

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

Płatność na żądanie bez klucza

Punkty końcowe o stałej cenie można płacić za jedno żądanie w czasie przez Lightning, bez klucza, bez konta i bez salda: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations I POST /embeddings. czat, odpowiedzi i wiadomości zawsze potrzebują klucza. prośba, która zawiera klucz jest naliczana do salda jak zwykle; przepływ płatności rozpoczyna się tylko wtedy, gdy klucz nie jest wysyłany.

Nymbot mówi dwie wersje tego samego pomysłu, z jednego backend: Lightning Labs' L402 (Również przyjęty pod jego starą nazwą, LSATi projekt IETF Płatność System uwierzytelniania HTTP z lightning Metoda i charge Używaj tego, co rozumie Twój klient.

Prowadzenie własnego serwera

Płatność bez klucza jest tylko wtedy, gdy API_L402_SECRET zawiera co najmniej 32 losowe bajty, takie jak hex (64 znaki) lub base64 (44). openssl rand -hex 32Krótsza lub domyślna wartość wyłącza funkcję i rejestruje dlaczego. API_L402_SECRET_PREVIOUS przez jeden dzień: zaświadczenia, adresy URL stanu i wyzwania, które zostały podjęte pod nim, działają do czasu ich wygaśnięcia.

Wyzwanie

Wyślij prośbę bez Authorization header. jeśli jest ważny, nic nie działa, a otrzymasz 402 Payment Required z fakturą za dokładnie to, co żądanie kosztuje: tę samą cenę klucz zapłaciłby, przekształcony w 10 sats standardowy kredyt lub 100 sats kredyt Pro i zaokrąglony do całego sat (co najmniej 1 sat, a co najmniej 0,05 minimum kredytu). WWW-Authenticate Wyzwania dla tej samej firmy:

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

Płatność request Parametr jest base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

Wyzwanie jest związane z punktem końcowym, do Content-Type (typ nośnika i, w przypadku wielu części, jego granica) i do SHA-256 dokładnych bajtów ciała, które wysłałeś, i trwa 15 minut. identyczne Ponowne żądanie: to samo Content-Type i te same bajty JSON, lub dla wielu punktów końcowych części (/images/edits, /audio/transcriptions, /audio/translationsWiększość bibliotek HTTP wybiera nową granicę za każdym razem, gdy kodują formularz, więc koduj go raz i wysyłaj te bajty dwa razy.

Każdy adres może prosić o 30 wyzwań na minutę (adres IPv6 liczy się jako cały /64). Wnioski, których adres nie jest znany, dzielą się bardziej rygorystycznym 10 na minutę, a istnieje ogólny limit na wyzwania Nymbot problemów na wszystkich adresach; żądanie odrzucone przed wyzwaniem jest wykonane (na przykład z ciałem, który nie jest ważny JSON) nie liczy się do niego. 429 z Retry-AfterPłatne punkty końcowe wywoływane bez klucza lub wiarygodności liczą się również w kierunku ogólnego limitu 120 nieautoryzowanych żądań na minutę na adres. Content-Type Nie jest application/json (lub multipart/form-data w odniesieniu do uploads) jest odmawiany 415 Nigdy nie otrzymuje faktury.

Embeddings są cenione na podstawie szacunku tokenów w wejściu, z marginą 1,5, ponieważ rzeczywista liczba jest znana dopiero później. Zwróć token.

Wysyłanie płatności

Zapłać fakturę za pomocą dowolnego portfela Lightning. Portfel daje preimage, 64 znaków sześciokątnych. Następnie wyślij to samo żądanie z jednym z tych:

schematuHeader
L402Authorization: L402 <macaroon>:<preimage> (LSAT Również działa)
PłatnośćAuthorization: Payment <base64url JSON>Gdzie znajduje się JSON {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

Płatne żądanie odpowiada dokładnie tak, jak żądanie wykonane za pomocą klucza, z wyjątkiem tego, że nymbot Obiekt nie posiada pól równowagi: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}i nie ma żadnego X-Nymbot-Balance-Sats żądanie zapłacone za pomocą systemu płatności otrzymuje również Payment-Receipt header (base64url JSON z idem wyzwania, hash płatności jako reference, status I timestampPłatne żądania nie są powiązane z żadnym nimem, więc nie pojawiają się w historii zapytań.

StatusKiedy
402 payment_already_usedOdpowiedź jest nowym wyzwaniem dla tego żądania, więc klient, który przechowuje w pamięci podręcznej swoją ostatnią wiarygodność (jak lnget I znowu po prostu pójdzie.
402 payment_mismatchUmowa została wydana na inny cel, Content-Type lub ciała, lub płacić mniej niż to żądanie teraz kosztuje. nowe wyzwanie dla tego żądania przychodzi z nim; jeśli płatność była zbyt mała, to, co zapłaciłeś, powraca jako Zwróć token (refund_token I refund_sats w naszym ciele).
402 payment_expiredPonad 15 minut minęło od wyzwania.Nowe wyzwanie towarzyszy.Jeśli wstępny obraz pokazuje, że zapłaciłeś, to, co zapłaciłeś, powraca jako wyzwanie. Zwróć token (refund_token I refund_sats w ciele), raz; potwierdzenie jest następnie wykorzystywane.
401 invalid_preimagePreimage nie hash do hash płatności faktury. płatność nie jest wykorzystywana.
401 invalid_payment_credentialWiarygodność jest wadliwa, została zmieniona po tym, jak Nymbot ją wydał, lub nazwy hash płatności Nymbot nigdy nie wystawił faktury.
429 rate_limit_exceededPonad 30 identyfikatorów lub kluczy, które nie udało się zweryfikować, pochodziło z tego adresu w ciągu minuty lub jeden token zwrotu został wysłany więcej niż 60 razy w ciągu minuty. 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"));

Klienci zbudowani mppx za pomocą metody Błyskawicy radzą sobie sami z wyzwaniem Płatności; wskaż je w punkcie końcowym i pozwól im zapłacić.

wideo

A płacone POST /videos Odpowiedzi 202 Podobnie jak jeden klucz, plus a status_url: GET Jest podpisany i działa przez 24 godziny, tak długo jak praca jest utrzymywana.

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

Trzymaj się refund_token z tej odpowiedzi: jest wyświetlany tylko tutaj. Jest pusty, podczas gdy wideo odwzorowuje (GET /api/v1/l402/refunds Odpowiedzi "status": "pending"); jeśli renderowanie nie zostanie rozliczone, płatność wyląduje na nim. refund_sats za zwróconą pracę, ale nigdy token, więc udostępnianie adresu URL stanu nie dzieli refundacji.

zwrotów

Jeśli płatne żądanie nie powiodło się, a dostawca naliczył Nymbot za próbę, płatność zostaje zachowana, a błąd mówi tak, z charged_sats, dokładnie tak samo jak w przypadku żądania kluczowego. Jeśli nie zostanie ona rozliczona, błąd Zwróć token Warto za co zapłaciłeś:

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

Nieużywane części powracają w ten sam sposób: jeśli poprosiłeś o dwa zdjęcia, a jeden nie został rozliczony, odpowiedź sukcesu nymbot obiekt nosi token zwrotu pieniędzy za brakujący; transkrypcja, której długości nie można odczytać z przodu, jest ceniona za najdłuższy, jaki może być plik (nigdy nie więcej niż 30 minut), a różnica w rzeczywistej długości powraca jako token zwrotu pieniędzy; jeśli okaże się, że jest dłuższy niż 30 minut, zostaje odrzucona 413 i cała płatność zostaje zwrócona. wstawki zwracają to, co szacuje się. nieudane wideo zwraca token, który zwrócono.

Token zwrotu pieniędzy jest przypadkowym 256-bitowym kodem. Nymbot przechowuje tylko jego hash i wygasa po 30 dniach.

  • Zapłać z nim. Wyślij Authorization: Bearer REFUND-… Poniżej przedstawiono kilka podstawowych zasad, które należy zastosować, aby wyeliminować zanieczyszczenia, a następnie wyeliminować zanieczyszczenia (refund_token_sats W tym nymbot Token o wartości mniejszej niż odpowiedź na żądanie 402 refund_insufficientNiewypłacona porażka stawia sats z powrotem na ten sam token.
  • Sprawdź to GET /api/v1/l402/refunds z tym samym headerem powraca {"sats": 237, "status": "open", "expires_at": "..."}.
  • Jeden token może być używany maksymalnie 60 razy na minutę.
  • Przenieś go do nym. Włóż go do Odzyskaj prezent w aplikacji Nymbot lub zadzwoń POST /api/v1/l402/refunds/redeem z a Podpisany wniosek I {"refund_token": "REFUND-...", "balance": "standard"} (lub "pro"Całe kredyty idą do salda (10 sats każdy na standardzie, 100 na Pro); stawki, które nie sprawiają, że cały kredyt pozostaje na tokenie dla żądań API.

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"

Odpowiedź

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