Base de coneixement Desenvolupadors
Balanç, top-ups i claus
Comproveu el que teniu, sobrepasseu Lightning, sobrepasseu automàticament des de la vostra cartera, vegeu el cost de cada sol·licitud i gestioneu les claus des del codi.
Aquesta pàgina està traduïda automàticament per comoditat. L'original en anglès és la versió que s'aplica.
Comprovar l'equilibri
Tant de les teves balances, com de la quantitat que s'utilitza la tapa d'aquesta clau.
GET https://nymbot.ai/api/v1/credits/balance Necessita una clau de foc. POST I també per als clients que s’ho esperen.
balance és els dos saldos junts en dòlars al preu actual de Bitcoin, per a eines que esperen un únic nombre (null La resta és en crèdits i sats, que és com es mantenen els saldos. key Una clau que ha arribat al seu cap encara pot comprovar el saldo.
Resposta
{
"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"
}
}
| Estatut | Quan |
|---|---|
401 | La clau és desapareguda, desconeguda, revocada o caducada. |
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);
Mètodes de pagament
Com es pot pujar, i els límits. llamp és l'únic mètode.
GET https://nymbot.ai/api/v1/topup/payment-methods Cap clau necessària.
Un top-up és de 10 a 1.000.000 sats; un top-up Pro ha de comprar almenys un crèdit Pro, de manera que comença a 100 sats. bulk_bonus enumera el crèdit addicional en els top-ups més grans, el mateix que en l'aplicació: 10%, 15% o 20% més en els top-ups estàndard de 500, 1.000 o 5.000 sats, i en els top-ups Pro de 5.000, 10.000 o 50.000 sats.
Resposta
{
"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);
Pujant sobre el llamp
Crea una factura de llamp que afegeix crèdit a la memòria a la qual pertany la clau. comprovació Per tenir el crèdit afegit.
POST https://nymbot.ai/api/v1/topup/create/btc-lightning Necessita una clau de foc.
| El camp | Tipus | Necessitat | Descripció |
|---|---|---|---|
amount | Número | Sí | Quants, en currencyUn nombre sencer per a l’aposta. |
currency | Línia | No | SATS (en el seu defecte) USD o BTCEls dòlars es converteixen al preu actual de Bitcoin. |
tier | Línia | No | pro (el defecte) o standard: a quin balanç va el crèdit. |
Un crèdit estàndard és 10 sats i un crèdit Pro 100 sats, més qualsevol bonificació a granel;
credits Diu el que afegirà aquesta factura.
Resposta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"payment_request": "lnbc100u1p5...",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": "2026-09-30T09:27:00Z",
"status": "pending"
}
| Estatut | Quan |
|---|---|
400 | Un altre mètode en el camí (unsupported_method), una moneda desconeguda (unsupported_currency) o nivell, una quantitat mancada, o una quantitat inferior al mínim (amount_too_small, per sobre de 1.000.000 de tasses (amount_too_large) o rebutjat per la cartera Lightning (amount_out_of_range). |
429 | Més de 60 factures per a aquest nim, o 120 des d'aquesta adreça, en una hora (rate_limit_exceeded, amb Retry-After). |
502 | No es pot fer cap factura en aquest moment (invoice_unavailable, amb 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);
Comprovar el top-up
Pregunta si la factura s'ha pagat i, una vegada que hi hagi, afegeix el crèdit. creditedComprovar de nou després és segur: el crèdit aterra una vegada, no importa quantes vegades es demani.
GET https://nymbot.ai/api/v1/topup/status/{invoice_id} necessita una clau de la nim que va fer la factura.
status És pending (Encara no s’ha pagat el preu) paid (Pagat, però encara no acreditat; tornar a comprovar), credited (en el seu balanç) o expired (no pagat en el temps).Els camps de balanç són per al nivell que arriba la factura.
Resposta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"status": "credited",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": null,
"balance_credits": 523.33,
"balance_sats": 52333
}
| Estatut | Quan |
|---|---|
400 | L'id no és l'id de 64 caràcters de la crida de creació. |
404 | No hi ha cap factura per aquest id per al teu nim (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);
Volem història
Una fila per sol·licitud: què era, quin model, quants tokens i què costa. No es conserven promptes ni respostes, de manera que no es retorna cap. Les files es conserven durant 90 dies, primer el més nou. Una clau que ha arribat al seu límit encara pot llegir el seu historial.
GET https://nymbot.ai/api/v1/queries/history necessita una clau d'API, que veu les seves pròpies sol·licituds, o una Sol·licitud signada de la teva nim, que veu cada clau.
| El camp | Tipus | Necessitat | Descripció |
|---|---|---|---|
page | Totes les preguntes (query) | No | Per defecte 1, amb un màxim de 1.000; una pàgina més alta és 400 invalid_value. |
page_count | Totes les preguntes (query) | No | Rams per pàgina. Default 20, a un màxim de 100. |
start_dateend_date | Càlcul (query) | No | ISO 8601 Dates i hores. |
model | Càlcul (query) | No | Només aquest model. |
type | Càlcul (query) | No | chat, responses, messages, image, video, speech, transcription o embedding. |
all_keys | booleà (query) | No | Amb una clau: true inclou totes les claus del mateix nym. false. |
key_id | Càlcul (query) | No | Amb una sol·licitud signada, o amb all_keys=trueNomés aquesta clau. |
Resposta
{
"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);
Signatura de sol·licituds de compte
La creació, canvi i revocació de claus, el resum del compte i els top-ups automàtics no prenen una clau de l'API. Prenen una signatura del seu nim, de manera que una clau perduda pot gastar fins al seu cap, però mai pot fer una altra clau o augmentar la seva pròpia cap.
L'aplicació fa això per a vostè: tot en la seva El foc El full utilitza aquests punts finals. Només necessites aquesta secció per gestionar les claus del teu propi codi.
La signatura és un esdeveniment Nostr del tipus 27235 (NIP-98), enviat base64-codificat en el
Authorization Header amb la paraula Nostr En el davant:
L’esdeveniment
{
"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és l'URL completa de la sol·licitud, la cadena de consulta inclosa, exactament com s'ha enviat.methodÉs el mètode HTTP.payloadés el SHA-256 del cos de la sol·licitud crua, en hexà.POSTiPATCH, i el cos que envieu ha de ser byte per byte el que heu hashat.created_atha de ser dins de 60 segons de l'horari del servidor.- Cada esdeveniment funciona una vegada, a
GETinclòs, de manera que un encapçalament capturat no es pugui reproduir. Signar un nou per a cada sol·licitud.nonceetiquetar amb un valor aleatori de manera que dues sol·licituds signades en el mateix segon encara siguin diferents. - El cos d'una sol·licitud signada pot tenir un màxim de 64 KB, i un cos necessita
Content-Type: application/json.
Un esdeveniment perdut torna 401 missing_nostr_authUna que està mal format, mal signat, massa vell, o per a una URL diferent, mètode o cos retorna
invalid_nostr_auth, amb la raó en el missatge; un reutilitzat un torna
nostr_auth_replayedLa signatura es comprova abans que es llegeixi el cos, i cada adreça pot fallar-la 30 vegades per minut (una adreça IPv6 compta com el seu conjunt /64); després d'això, obté el 429 amb
Retry-After.
Els navegadors poden trucar a aquests terminis només des dels llocs propis de Nymbot (https://nymbot.ai,
https://nymchat.app Una pàgina en qualsevol altre lloc no rep cap encapçalament CORS, de manera que no pot llegir el que torna.
OriginNo estan afectats.
La signatura necessita la clau secreta del teu nim (la clau nsec), que controla tot: la teva identitat, el teu historial i el teu balanç. Només posar-ho en un guió en una màquina de confiança, llegir-ho des de l'entorn en comptes d'escriure'l al fitxer, i preferir l'aplicació quan puguis.
Aquests ajudants construeixen l'encapçalament. Els exemples posteriors d'aquesta pàgina els utilitzen. NOSTR_SECRET_HEX· El cURL utilitza el
Nàstic eina de línia de comandes, que pren una clau nsec o hexagonal, i sha256sum En el 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");
}
Resum del compte
El que mostra la fitxa de l'API de l'aplicació a la part superior: la teva clau pública, ambdues balances, quantes claus estan actives (no revocades o expirades), i la Top-up automàtic Instal·lacions o
null quan el servidor no els ofereix.
GET https://nymbot.ai/api/v1/account Necessitem a Sol·licitud signada.
Resposta
{
"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());
Gestió de claus
Els punts finals darrere de la llista de claus de l'aplicació. Tots ells necessiten una Sol·licitud signadaCada clau es retorna en aquest formulari, amb temps en ISO 8601 i quantitats en sats:
Objecte clau
{
"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 És suficient per reconèixer una clau però no per utilitzar-la.La clau mateixa només es retorna una vegada, quan es fa.
Llista de claus
GET https://nymbot.ai/api/v1/keys Es va signar.
| El camp | Tipus | Necessitat | Descripció |
|---|---|---|---|
include_revoked | booleà (query) | No | Inclou les claus revocades. Default false. |
Resposta
{ "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);
Fer una clau
POST https://nymbot.ai/api/v1/keys Inscripcions - Retorn 201.
| El camp | Tipus | Necessitat | Descripció |
|---|---|---|---|
name | Línia | Sí | 1 a 40 caràcters, diferents de les altres claus actives (ignorar el cas). |
limit_sats | íntegre | No | El cap de despesa en sats, almenys 1. |
reset_period | Línia | No | daily, weekly o monthlyNecessitats limit_satsDeixeu-ho fora per a una tapa que mai es restableix. |
expire_at | Línia o íntegre | No | Quan la clau deixa de funcionar: un temps ISO 8601, o mil·lisegons des de 1970. |
La resposta (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"
}
}
| Estatut | Quan |
|---|---|
400 | Un nom perdut o massa llarg; un nom ja en ús (duplicate_name); un cap que no és un nombre sencer d'almenys 1; un període de restauració sense cap; una expiració en el passat; un camp desconegut (unknown_parameter); o 25 claus actives ja (too_many_keys). |
429 | Més de 60 claus fetes per aquest nim, o 120 d'aquesta adreça, en una hora (rate_limit_exceeded, amb 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);
Llegir una clau
GET https://nymbot.ai/api/v1/keys/{id} Es va signar.
Retorns {"data": {…}} amb l’objecte clau, o 404
key_not_found si cap de les teves claus té aquesta identificació.
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);
Canviar la clau
PATCH https://nymbot.ai/api/v1/keys/{id} Es va signar.
Envia qualsevol name, limit_sats, reset_period i
expire_at, amb les mateixes regles que quan es fa una clau. null No hi ha cap límit, no hi ha cap reset, no hi ha expiració. Canviar el període de reset comença un nou període a zero.400 key_revoked• Retorns
{"data": {…}} amb l'objecte clau actualitzat.
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);
Revocació de la clau
DELETE https://nymbot.ai/api/v1/keys/{id} Es va signar.
Es tanca la clau de seguida, per bé. Es queda a la llista amb revoked_at es troba, i es pot veure amb include_revoked=trueRevocar una clau que ja ha estat revocada respon de la mateixa manera. Només es conserven les últimes 50 claus revocades; les més antigues s'esborren quan es revoca una altra clau.
Resposta
{ "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());
El nou NWC Auto-top-up
Connecta una cartera de llamp amb Nostr Wallet Connect i Nymbot arriba a un saldo per si mateix quan la despesa de l'API s'executa baix. Sol·licitud signada.
Com funciona: després que una sol·licitud d'API es carregui al saldo que has triat per veure, si aquest saldo ha caigut per sota del teu llindar, Nymbot fa una factura per la teva quantitat superior, demana a la teva cartera que la pagui, i afegeix el crèdit. S'acumula al màxim una vegada cada 5 minuts per a cada nim i saldo, de manera que una explosió de sol·licituds no pot esborrar la cartera. La despesa en les aplicacions no la desencadena. El temps i la mida de l'últim top-up, i l'últim error, estan en la configuració; si un pagament va passar després d'un error, comproveu la seva factura amb Situació top-up El crèdit.
Nymbot l'emmagatzema encriptat i només l'utilitza per pagar les seves pròpies factures, però fa una connexió només per això, amb un pressupost de despesa a la seva cartera, de manera que el més que mai podria pagar és un nombre que vostè va triar. pay_invoice.
Connectar una cartera
POST https://nymbot.ai/api/v1/nwc-auto-topup/connect Es va signar.
| El camp | Tipus | Necessitat | Descripció |
|---|---|---|---|
nwc_url | Línia | Sí | La cadena de connexió, que comença nostr+walletconnect://Nymbot demana la cartera per get_info abans de guardar-lo i emmagatzemar-lo encriptat. |
threshold_sats | íntegre | Sí | Supera quan el saldo cau per sota d'aquests molts sats. almenys 1.000. |
topup_sats | íntegre | Sí | Quant s'ha d'afegir cada vegada. 1.000 a 1.000.000 apostes. |
tier | Línia | No | pro (el defecte) o standard: l'equilibri per mirar i pujar. |
Resposta
{
"data": {
"connected": true,
"threshold_sats": 5000,
"topup_sats": 20000,
"tier": "pro",
"last_topup_at": null,
"last_topup_sats": null,
"last_error": null
}
}
| Estatut | Quan |
|---|---|
400 | No hi ha una cadena de connexió (invalid_nwc_url); la cartera no va respondre sobre el seu relleu (nwc_unreachable) o va rebutjar el control (nwc_rejected); la connexió no pot pagar les factures (nwc_missing_permissiono una quantitat fora dels límits. |
501 | No s'ha activat el top-up automàtic per a aquest servidor (nwc_unavailableEl mateix passa amb els altres dos terminis. |
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());
Llegir les configuracions
GET https://nymbot.ai/api/v1/nwc-auto-topup Es va signar.
Retorna el mateix objecte que la connexió, amb connected: false I els altres camps
null Quan no hi ha cartera connectada, la cadena de connexió en si no es torna mai.
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());
Desconnexió
DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection Es va signar.
Elimina la cadena de connexió emmagatzemada. No es fan més top-ups. Per estar segur, també podeu revocar la connexió a la cartera.
Resposta
{ "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());
Pagament a petició sense clau
Els punts finals de preu fix es poden pagar per una sol·licitud alhora a través de Lightning, sense clau, sense compte i sense saldo: POST /images/generations, POST /images/edits,
POST /videos, POST /audio/speech, POST /audio/transcriptions,
POST /audio/translations i POST /embeddingsUna sol·licitud que porta una clau es factura al saldo com de costum; el flux de pagament només comença quan no s'envia cap clau.
Nymbot parla de dues versions de la mateixa idea, des d'un backend: Lightning Labs'
El 402 (també acceptat sota el seu antic nom, LSAT) i el projecte IETF
Pagament L’autenticació HTTP amb el lightning Mètode i
charge Utilitza el que el teu client entengui.
Pagar sense clau és en només quan API_L402_SECRET conté almenys 32 bytes aleatoris, com a hex (64 caràcters) o base64 (44). openssl rand -hex 32Un valor més curt o predictible desactiva la funció i registra per què. API_L402_SECRET_PREVIOUS durant un dia: les credencials, les URL d'estat i els reptes realitzats en virtut d'això continuen funcionant fins que expiren.
El repte
Envia la sol·licitud amb no Authorization si és vàlid, res no s'executa, i s'obté 402 Payment Required amb una factura per exactament el que costa aquesta sol·licitud: el mateix preu que pagaria una clau, convertit a 10 sats un crèdit estàndard o 100 sats un crèdit Pro i arrodonit fins a un sat sencer (almenys 1 sat, i almenys el mínim de crèdit 0,05). WWW-Authenticate Recomanacions per a la mateixa factura:
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 }
}
El pagament request El paràmetre és base64url JSON:
{"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.
Un repte està lligat al punt final, al Content-Type (el seu tipus de mitjans i, per a múltiples parts, el seu límit) i al SHA-256 del cos exacte dels bytes que ha enviat, i dura 15 minuts. idèntica Reiteració: El mateix
Content-Type i els mateixos bytes JSON, o per als punts finals multiparts (/images/edits, /audio/transcriptions, /audio/translationsLa majoria de biblioteques HTTP trien un nou límit cada vegada que codifiquen un formulari, així que el codifiquen una vegada i envien aquests bytes dues vegades.
Cada adreça pot demanar 30 reptes per minut (una adreça IPv6 compta com el seu conjunt /64). Les sol·licituds l'adreça de les quals no és coneguda comparteixen un 10 més estricte per minut, i hi ha un límit global en els reptes Nymbot problemes a través de totes les adreces; una sol·licitud rebutjada abans d'un desafiament es fa (per exemple, amb un cos que no és vàlid JSON) no compta cap a ella. 429 amb Retry-AfterEls terminis de pagament trucats sense clau o credencial també compten cap al límit general de 120 sol·licituds no autenticades per minut per adreça. Content-Type No és application/json (o el
multipart/form-data per a subministraments) es refusa amb 415 Mai no obté una factura.
Els embeddings són preuats a partir d'una estimació dels tokens en l'entrada, amb un marge de 1,5 vegades, ja que el recompte real només es coneix després. Recuperació de token.
Enviar el pagament
Pagar la factura amb qualsevol cartera de llamp. La cartera li dóna la preimatge, 64 caràcters hexàtics. Llavors enviar la mateixa sol·licitud amb un d'aquests:
| Esquema | Header |
|---|---|
| El 402 | Authorization: L402 <macaroon>:<preimage> (LSAT També es treballa) |
| Pagament | Authorization: Payment <base64url JSON>On es troba el JSON {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}} |
Una sol·licitud de pagament respon exactament com una sol·licitud feta amb una clau, excepte que la nymbot
L'objecte no té camps d'equilibri: {"payment": "l402", "tier": "pro", "paid_sats": 237,
"charged_sats": 237}I no hi ha X-Nymbot-Balance-Sats Una sol·licitud pagada amb l'esquema de pagament també rep una Payment-Receipt header (base64url JSON amb el desafiament id, el pagament hash com reference, status i
timestampLes sol·licituds pagades no estan lligades a cap nim, de manera que no apareixen en l'historial de consultes.
| Estatut | Quan |
|---|---|
402 payment_already_used | Cada pagament paga per una sol·licitud.La resposta és un nou repte per a aquesta sol·licitud, de manera que un client que cache la seva última credencial (com lnget Només vol tornar a pujar. |
402 payment_mismatch | La credencial es va emetre per a un altre punt final, Content-Type o cos, o paga menys del que aquesta sol·licitud ara costa. Un nou desafiament per a aquesta sol·licitud ve amb ell; si el pagament va ser massa petit, el que va pagar es torna com un Recuperació de token (refund_token i refund_sats en el cos) |
402 payment_expired | Més de 15 minuts han passat des del repte. Un nou repte ve amb ell. Si la preimatge mostra que has pagat, el que has pagat torna com un repte. Recuperació de token (refund_token i refund_sats en el cos), una vegada; el credencial es fa servir. |
401 invalid_preimage | La preimatge no hash a la hash de pagament de la factura. El pagament no s'utilitza. |
401 invalid_payment_credential | La credencial està mal formada, es va canviar després que Nymbot la va emetre, o el nom d'un hash de pagament que Nymbot mai no va emetre una factura. |
429 rate_limit_exceeded | Més de 30 credencials o claus que no es van verificar van arribar d'aquesta adreça en un minut, o un token de reemborsament es va enviar més de 60 vegades en un minut. 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"));
Els clients es construeixen mppx amb un mètode de llamp gestionar el desafiament de pagament ells mateixos; assenyalar-los al punt final i deixar-los pagar.
Vídeo
El pagament POST /videos Respostes 202 com una clau, més a
status_url: GET Es signa i funciona durant 24 hores, sempre que es mantingui la feina.
{
"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..."
}
}
Mantingui el refund_token A partir d'aquesta resposta: només es mostra aquí. Està buit mentre que el vídeoGET /api/v1/l402/refunds Respostes "status": "pending"); si el rendiment fracassa sense facturar, el pagament aterra en ell.
refund_sats per a un treball reemborsat però mai el token, de manera que compartir la URL d'estat no comparteix el reemborsament.
Reemborsament
Si una sol·licitud de pagament fracassa i el proveïdor factura Nymbot per l'intent, el pagament es manté i l'error diu que ho és, amb charged_sats, exactament com per a una sol·licitud clau. Si fracassa sense ser facturat, l'error porta una Recuperació de token Val la pena el que hagis pagat:
{
"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"
}
}
Les parts no utilitzades tornen de la mateixa manera: si heu demanat dues imatges i una no ha estat facturada, la resposta d'èxit nymbot l'objecte porta un token de reemborsament per al que falta; una transcripció la longitud de la qual no es podia llegir abans es preu per el més llarg que podia ser el fitxer (mai més de 30 minuts), i la diferència a la longitud real es torna com un token de reemborsament; si resulta ser més llarg de 30 minuts, es nega amb 413
Els embeddings retornen el que l'estimació va mantenir. Un vídeo fallit reemborsarà el token que va retornar la seva presentació.
Un token de reemborsament és un codi aleatori de 256 bits. Nymbot només emmagatzema el seu hash, i expira després de 30 dies.
- Pagueu amb això. Envia
Authorization: Bearer REFUND-…En aquest cas, el preu de la targeta és el mateix que el preu de la targeta, i el preu és el mateix que el de la targeta (refund_token_satsEn lanymbotUn token que val menys que les respostes a la sol·licitud402refund_insufficientUn fracàs no comptabilitzat posa la taxa de tornada en el mateix token. - Comproveu el
GET /api/v1/l402/refundsamb el mateix header torna{"sats": 237, "status": "open", "expires_at": "..."}. - Un token es pot utilitzar un màxim de 60 vegades per minut.
- Poseu-la en una nit. Posa’l en Recuperar un regal a l'aplicació Nymbot, o trucar
POST /api/v1/l402/refunds/redeemAmb a Sol·licitud signada i{"refund_token": "REFUND-...", "balance": "standard"}(o el"pro"Els crèdits sencers van al saldo (10 sats cadascun en estàndard, 100 en Pro); les apostes que no fan que un crèdit sencer es quedi en el token per a les sol·licituds d'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"
Resposta
{ "data": { "credited": 23, "tier": "standard", "balance_credits": 123, "remaining_sats": 7 } }