Base de coñecemento desenvolvedores
Balance, top-ups e chaves
Comprobe o que ten, top-up sobre Lightning, top-up automaticamente a partir da súa propia carteira, ver o que cada solicitude custa, e xestionar as claves a partir de código.
Esta páxina está traducida por máquina para maior comodidade. O orixinal en inglés é a versión que se aplica.
Comprobar o equilibrio
Ambos os seus saldos, e canto da capa desta chave é usada.
GET https://nymbot.ai/api/v1/credits/balance Necesita unha chave de API. POST Tamén funciona, para os clientes que o esperan.
balance é os dous saldos xuntos en dólares ao prezo actual de Bitcoin, para ferramentas que esperan un único número (null O resto está en créditos e sats, que é como os saldos son realmente mantidos. key Describe a clave que solicitou.Unha clave que alcanzou o seu límite aínda pode comprobar o 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"
}
}
| Estatuto | cando |
|---|---|
401 | A clave está ausente, descoñecida, revogada ou 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étodos de pagamento
Como pode top up, e os límites. Lightning é o único método.
GET https://nymbot.ai/api/v1/topup/payment-methods Non é necesaria ningunha chave.
Un top-up é de 10 a 1.000.000 sats; un top-up Pro ten que mercar polo menos un crédito Pro, polo que comeza en 100 sats. bulk_bonus lista o crédito extra en top-ups máis grandes, o mesmo que na aplicación: 10%, 15% ou 20% máis en top-ups estándar de 500, 1.000 ou 5.000 sats, e en top-ups Pro de 5.000, 10.000 ou 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);
Subindo sobre o lóstrego
Realiza unha factura de lóstrego que engade crédito ao ninja á que pertence a clave. comprobalo para ter o crédito engadido.
POST https://nymbot.ai/api/v1/topup/create/btc-lightning Necesita unha chave de API.
| Campo | Tipo | Necesario | Descrición |
|---|---|---|---|
amount | Número | Si | Canto máis, en currencyUn número enteiro para a aposta. |
currency | unha string | non | SATS (A miña debilidade) USD ou BTCOs dólares son convertidos ao prezo actual de Bitcoin. |
tier | unha string | non | pro (O defecto ou standard: a que balance vai o crédito. |
Un crédito estándar é 10 sats e un crédito Pro 100 sats, máis calquera bonos en masa;
credits Diga o que esta factura vai engadir.
Resposta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"payment_request": "lnbc100u1p5...",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": "2026-09-30T09:27:00Z",
"status": "pending"
}
| Estatuto | cando |
|---|---|
400 | Outro método no camiño (unsupported_method), unha moeda descoñecida (unsupported_currency) ou nivel, unha cantidade faltante, ou unha cantidade por debaixo do mínimo (amount_too_smallMáis de 1 millón (amount_too_large) ou rexeitado pola carteira Lightning (amount_out_of_range). |
429 | Máis de 60 facturas para este nim, ou 120 desde esta dirección, nunha hora (rate_limit_exceeded, con Retry-After). |
502 | Non se pode facer unha factura no momento (invoice_unavailable, con 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);
Coñecer un top-up
Pregunta se a factura foi pagada e, unha vez que ten, engade o crédito. creditedComprobar de novo despois é seguro: o crédito aterra unha vez, por moitas veces que pida.
GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - Necesita unha chave do nim que fixo a factura.
status é pending (aínda non se pagou) paid (Pago, pero aínda non creditado; verifique de novo), credited (no seu balance) ou expired (Non se paga no tempo).Os campos de balance son para o nivel no que se cobre a factura.
Resposta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"status": "credited",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": null,
"balance_credits": 523.33,
"balance_sats": 52333
}
| Estatuto | cando |
|---|---|
400 | O id non é o id de 64 caracteres da chamada creada. |
404 | Ningunha factura por ese id para o 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);
Queremos historia
Unha liña por solicitude: o que era, que modelo, cantos tokens e o que custou. Non se conservan prompts ou respostas, polo que non se devolven. As filas consérvanse durante 90 días, o máis recente primeiro. Unha clave que alcanzou o seu límite aínda pode ler o seu historial.
GET https://nymbot.ai/api/v1/queries/history precisa dunha clave de API, que ve as súas propias solicitudes, ou unha Solicitude asinada da túa ninfa, que ve cada chave.
| Campo | Tipo | Necesario | Descrición |
|---|---|---|---|
page | Xénero integer (query) | non | Por defecto 1, como máximo 1.000; unha páxina máis alta é 400 invalid_value. |
page_count | Xénero integer (query) | non | Roldas por páxina. 20 por defecto, ata un máximo de 100. |
start_dateend_date | Conexión (query) | non | ISO 8601 datas ou horas. |
model | Conexión (query) | non | Só este modelo. |
type | Conexión (query) | non | chat, responses, messages, image, video, speech, transcription ou embedding. |
all_keys | galego (query) | non | cunha chave: true inclúe todas as claves do mesmo nym. false. |
key_id | Conexión (query) | non | mediante solicitude asinada ou mediante all_keys=trueSó esta chave. |
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);
Solicitacións de conta
A creación, o cambio e a revogación de chaves, o resumo da conta e os top-ups automáticos non toman unha clave de API. Eles toman unha sinatura do seu nim, polo que unha clave perdida pode gastar ata o seu límite, pero nunca pode facer outra clave ou aumentar o seu propio límite.
A aplicación fai isto por ti: todo na súa fogueira Sheet usa estes puntos finais. Só precisa desta sección para xestionar as claves do seu propio código.
A sinatura é un evento Nostr do tipo 27235 (NIP-98), enviado base64-codificado na
Authorization Header coa palabra Nostr Na súa fronte:
O evento
{
"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é a URL completa da solicitude, incluída a cadea de consulta, exactamente como foi enviada.methodÉ o método HTTP.payloadé o SHA-256 do corpo de solicitude bruto, en hexadecimal.POSTePATCH, e o corpo que envía debe ser byte por byte o que hashou.created_atDebe estar dentro de 60 segundos do reloxo do servidor.- Cada evento funciona unha vez, a
GETincluído, polo que un encabezado capturado non pode ser reproducido. Asine un novo para cada solicitude.nonceetiqueta cun valor aleatorio para que dúas solicitudes asinadas no mesmo segundo aínda sexan diferentes. - O corpo dunha solicitude asinada pode ter un máximo de 64 KB, e un corpo necesita
Content-Type: application/json.
Un evento perdido regresa 401 missing_nostr_authUn que está mal formado, mal asinado, moi antigo, ou para unha URL diferente, método ou corpo devolve
invalid_nostr_auth, coa razón na mensaxe; un reutilizado un retorna
nostr_auth_replayedA sinatura é verificada antes de que o corpo sexa lido, e cada enderezo pode fallar 30 veces por minuto (un enderezo IPv6 conta como o seu enteiro /64); despois recibe 429 con
Retry-After.
Os navegadores só poden chamar estes puntos finais desde os propios sitios de Nymbot (https://nymbot.ai,
https://nymchat.app Unha páxina en calquera outro sitio non recibe os encabezados CORS de volta, polo que non pode ler o que devolven.
OriginNon están afectados.
A sinatura necesita a clave secreta do seu nim (o nsec), que controla todo: a túa identidade, o teu historial e o teu saldo.Só o pon nun guión nunha máquina en que confías, lea-lo desde o ambiente en lugar de escribilo no ficheiro, e prefire a aplicación cando poidas.
Estes axudantes constrúen o encabezado.Os exemplos posteriores nesta páxina utilízanse. NOSTR_SECRET_HEX; o cURL un usa o
Neno ferramenta de liña de comandos, que toma unha clave nsec ou hexadecimal, e sha256sum para os 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");
}
Resumo da conta
O que a folla de API da aplicación mostra na parte superior: a súa clave pública, ambos os equilibrios, cantas claves están activas (non revocadas ou expiradas) e o número de Top-up automático configuración, ou
null cando o servidor non os ofrece.
GET https://nymbot.ai/api/v1/account Necesito unha Solicitude asinada.
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());
Xestión de chaves
Os puntos finais detrás da lista de claves da aplicación. Todos eles necesitan unha Solicitude asinadaCada clave é devolta nesta forma, con tempos en ISO 8601 e cantidades en sats:
Obxecto clave
{
"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 É suficiente para recoñecer unha clave pero non para usala.A clave en si só se devolve unha vez, cando se fai.
Listaxe de chaves
GET https://nymbot.ai/api/v1/keys e asinaron.
| Campo | Tipo | Necesario | Descrición |
|---|---|---|---|
include_revoked | galego (query) | non | Inclúe as claves revocadas. 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);
Facer unha chave
POST https://nymbot.ai/api/v1/keys Asinado, devolto 201.
| Campo | Tipo | Necesario | Descrición |
|---|---|---|---|
name | unha string | Si | 1 a 40 caracteres, diferentes das súas outras claves activas (ignorando o caso). |
limit_sats | enteiro | non | O límite de gastos en sats, polo menos 1. deixalo fóra por ningún cap. |
reset_period | unha string | non | daily, weekly ou monthlyNecesidades limit_satsDeixalo para unha tapa que nunca se restaura. |
expire_at | String ou enteiro | non | Cando a clave deixa de funcionar: unha ISO 8601 veces, ou milisegundos desde 1970. |
Resposta á pregunta (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"
}
}
| Estatuto | cando |
|---|---|
400 | Un nome perdido ou demasiado longo; un nome xa en uso (duplicate_name); un cap que non é un número enteiro de polo menos 1; un período de restauración sen cap; unha expiración no pasado; un campo descoñecido (unknown_parameter); ou 25 chaves activas xa (too_many_keys). |
429 | Máis de 60 chaves feitas por este nim, ou 120 desde esta dirección, nunha hora (rate_limit_exceeded, con 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);
Ler unha chave
GET https://nymbot.ai/api/v1/keys/{id} e asinaron.
Regreso {"data": {…}} co obxecto clave, ou 404
key_not_found Se a túa clave non ten esa identificación.
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);
Cambiar unha chave
PATCH https://nymbot.ai/api/v1/keys/{id} e asinaron.
Envíanos calquera name, limit_sats, reset_period e
expire_at, coas mesmas regras que cando se fai unha chave. null Cambiar o período de reposición comeza un novo período a cero. (400 key_revoked) Regreso
{"data": {…}} co obxecto clave actualizado.
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);
Revogación dunha chave
DELETE https://nymbot.ai/api/v1/keys/{id} e asinaron.
Detén a clave de inmediato, para ben. revoked_at Aínda así, e pódese ver con include_revoked=trueA revogación dunha clave que xa foi revogada responde do mesmo xeito. Só se conservan as máis recentes 50 chaves revogadas; as máis antigas se borran cando se revoga outra clave.
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());
NWC Auto-top-up en liña
Conecta unha carteira Lightning con Nostr Wallet Connect e Nymbot cumpre un saldo por si só cando o gasto en API é baixo. A folla de API da aplicación ten as mesmas opcións; estes son os puntos finais detrás dela. Solicitude asinada.
Como funciona: despois de que unha solicitude de API se cargue no saldo que escolleu para ver, se ese saldo caeu por baixo do seu limiar, Nymbot fai unha factura para o seu importe superior, pide que a súa carteira o pague e engade o crédito. Supera o máximo unha vez cada 5 minutos para cada nim e saldo, de xeito que unha explosión de solicitudes non pode drenar a carteira. O gasto nas aplicacións non o desencadea. O tempo e o tamaño do último top-up, e o último erro, están nas configuracións; se un pagamento pasou despois dun erro, verifique a súa factura con O estado top-up Créditos para iso.
Nymbot almacena cifrado e só o usa para pagar as súas propias facturas, pero fai unha conexión só para iso, cun orzamento de gasto na súa carteira, polo que o máis que pode pagar é un número escollido por vostede. pay_invoice.
Conectar unha carteira
POST https://nymbot.ai/api/v1/nwc-auto-topup/connect e asinaron.
| Campo | Tipo | Necesario | Descrición |
|---|---|---|---|
nwc_url | unha string | Si | A cadea de conexión, comezando nostr+walletconnect://Nymbot pide a carteira para get_info antes de gardalo, e almacena cifrado. |
threshold_sats | enteiro | Si | Top up cando o saldo cae por baixo destes moitos sats. polo menos 1.000. |
topup_sats | enteiro | Si | Canto engadir cada vez. 1.000 a 1.000.000 sats. |
tier | unha string | non | pro (O defecto ou standard: o equilibrio para mirar e top up. |
Resposta
{
"data": {
"connected": true,
"threshold_sats": 5000,
"topup_sats": 20000,
"tier": "pro",
"last_topup_at": null,
"last_topup_sats": null,
"last_error": null
}
}
| Estatuto | cando |
|---|---|
400 | Non é unha liña de conexión (invalid_nwc_url(A páxina non responde á súa mensaxe)nwc_unreachable) ou rexeitou o control (nwc_rejected); a conexión non pode pagar facturas (nwc_missing_permission); ou unha cantidade fóra dos límites. |
501 | Non se activan as top-ups automáticas para este servidor (nwc_unavailableO mesmo se aplica aos outros dous puntos finais. |
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());
Ler as configuracións
GET https://nymbot.ai/api/v1/nwc-auto-topup e asinaron.
Devolve o mesmo obxecto que conectar, con connected: false e os outros campos
null Cando non hai ningunha carteira conectada, a cadea de conexión en si nunca se devolve.
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());
Desconexión
DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection e asinaron.
Elimina a cadea de conexión almacenada. Non se realizan máis top-ups. Para estar seguro, tamén pode revogar a conexión na súa carteira.
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());
Pagar por solicitude sen unha chave
Os puntos finais de prezo fixo poden ser pagados por unha solicitude ao mesmo tempo en Lightning, sen clave, sen conta e sen saldo: POST /images/generations, POST /images/edits,
POST /videos, POST /audio/speech, POST /audio/transcriptions,
POST /audio/translations e POST /embeddingsChat, respostas e mensaxes sempre necesitan unha clave. Unha solicitude que leva unha clave é facturada ao saldo como de costume; o fluxo de pagamento só comeza cando non se envía ningunha clave.
Nymbot fala de dúas versións da mesma idea, desde un backend: Lightning Labs
Xénero 402 (tamén aceptado baixo o seu antigo nome, LSAT) e o proxecto IETF
Pago O esquema de autenticación HTTP coa lightning Método e
charge Use o que o seu cliente entenda.
Pagar sen unha chave é só en cando API_L402_SECRET ten polo menos 32 bytes aleatorios, como hexadecimal (64 caracteres) ou base64 (44). openssl rand -hex 32Un valor máis curto ou adiviñable apaga a característica e rexistra por que. API_L402_SECRET_PREVIOUS durante un día: credenciais, URLs de estado e desafíos feitos baixo el continúan funcionando ata que expiran.
O reto
Envía a túa solicitude con NON Authorization se é válido, nada se executa, e obtés 402 Payment Required cunha factura para exactamente o que a solicitude custa: o mesmo prezo que pagaría unha clave, convertido en 10 sats un crédito estándar ou 100 sats un crédito Pro e arredondado ata un sat enteiro (polo menos 1 sat, e polo menos o mínimo de crédito 0,05). WWW-Authenticate Recomendacións para a mesma 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 }
}
O pago request O parámetro é base64url JSON:
{"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.
Un reto está ligado ao punto final, ao Content-Type (o seu tipo de soporte e, para multipart, o seu límite) e ao SHA-256 dos bytes corporais exactos que enviou, e dura 15 minutos. identidade Unha vez máis: o mesmo
Content-Type e os mesmos bytes JSON, ou para os puntos finais de varias partes (/images/edits, /audio/transcriptions, /audio/translationsA maioría das bibliotecas HTTP elixen un novo límite cada vez que codifican un formulario, polo que codifícao unha vez e envía eses bytes dúas veces.
Cada enderezo pode pedir 30 desafíos por minuto (un enderezo IPv6 conta como o seu enteiro /64). As solicitudes cuxo enderezo non é coñecido comparten un 10 máis estricto por minuto, e hai un límite global sobre os desafíos Nymbot problemas en todos os enderezos; unha solicitude rexeitada antes de que se faga un desafío (por exemplo, cun corpo que non é válido JSON) non conta para el. 429 con Retry-AfterOs puntos finais pagos chamados sen unha clave ou credencial tamén contan cara ao límite xeral de 120 solicitudes non autenticadas por minuto por enderezo. Content-Type Non é application/json ou
multipart/form-data para cargas) é rexeitado con 415 e nunca recibe unha factura.
As incorporacións son prezadas a partir dunha estimación dos tokens na entrada, cunha marxe de 1,5 veces, xa que a conta real só se coñece despois. Recuperación de tokens.
Envío do pagamento
Pague a factura con calquera carteira Lightning. A carteira dálle a imaxe previa, 64 caracteres hexadecimales. A continuación, envíe a mesma solicitude cun destes:
| esquema | Header |
|---|---|
| Xénero 402 | Authorization: L402 <macaroon>:<preimage> (LSAT que tamén funciona) |
| Pago | Authorization: Payment <base64url JSON>, onde o JSON está {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}} |
Unha solicitude paga responde exactamente como unha solicitude feita cunha chave, agás que o nymbot
O obxecto non ten campos de equilibrio: {"payment": "l402", "tier": "pro", "paid_sats": 237,
"charged_sats": 237}e non hai ningunha X-Nymbot-Balance-Sats unha solicitude pagada co esquema de pagamento tamén recibe un Payment-Receipt header (base64url JSON co id de desafío, o hash de pagamento como reference, status e
timestampAs solicitudes pagas non están vinculadas a ningún nim, polo que non aparecen no historial de consultas.
| Estatuto | cando |
|---|---|
402 payment_already_used | Cada pago paga por unha solicitude.A resposta é un novo reto para esta solicitude, polo que un cliente que cache a súa última credencial (como lnget O que pasa é que simplemente volven a nacer. |
402 payment_mismatch | A credencial foi emitida para outro punto final, Content-Type ou corpo, ou paga menos do que esta solicitude agora custa. un novo desafío para esta solicitude vén con el; se o pagamento foi demasiado pequeno, o que pagou volve como un Recuperación de tokens (refund_token e refund_sats no seu corpo). |
402 payment_expired | Máis de 15 minutos pasaron desde o desafío.Un novo desafío vén con el. Se a preimpresión mostra que pagou, o que pagou volve como un Recuperación de tokens (refund_token e refund_sats no corpo), unha vez; a credencial é entón usada. |
401 invalid_preimage | A preimpresión non hash ao hash de pagamento da factura. |
401 invalid_payment_credential | A credencial está mal formada, foi cambiada despois de que Nymbot a emitise, ou nomea un hash de pagamento para o que Nymbot nunca emitiu unha factura. |
429 rate_limit_exceeded | Máis de 30 credenciais ou chaves que non foron verificadas chegaron deste enderezo nun minuto, ou un token de reembolso foi enviado máis de 60 veces nun minuto. 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"));
Os clientes están construídos mppx cun método Lightning xestionar o reto de pagamento eles mesmos; apunta-los no punto final e deixalos pagar.
Vídeos
Unha paga POST /videos Respostas 202 como unha chave, máis a
status_url: GET É asinado e funciona durante 24 horas, mentres se manteña o traballo.
{
"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..."
}
}
Manteña o refund_token Aínda que non está dispoñible, o vídeo está dispoñible para descargar (GET /api/v1/l402/refunds Respostas "status": "pending"); se o rendemento falla sen facturar, o pagamento aterra nel. A URL de estado mostra
refund_sats para un traballo reembolsado pero nunca o token, así que compartir a URL de estado non comparte o reembolso.
Reembolsos
Se unha solicitude paga fracasou e o provedor cobrou Nymbot polo intento, o pagamento mantense e o erro di así, con charged_sats, exactamente como para unha solicitude clave. Se falla sen ser facturado, o erro leva un Recuperación de tokens Vale o que pagaches:
{
"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"
}
}
As pezas non utilizadas volven do mesmo xeito: se pediches dúas imaxes e unha non foi descargada, a resposta de éxito nymbot O obxecto leva un token de reembolso para o que falta; unha transcrición cuxa lonxitude non puidese ser lida antes é prezada polo máis longo que puidese ser o ficheiro (nunca máis de 30 minutos), e a diferenza á lonxitude real volve como un token de reembolso; se resulta ser máis longo que 30 minutos, é rexeitado con 413
Os embeddings devolven o que a estimación mantiña de volta.Un vídeo fallado devolve ao token que a súa presentación devolveu.
Un token de reembolso é un código aleatorio de 256 bits. Nymbot só almacena o seu hash, e expira despois de 30 días.
- Paga con iso. Envío
Authorization: Bearer REFUND-…En calquera caso, o prezo da tarxeta de crédito é o mesmo que o prezo da tarxeta de crédito. (refund_token_satsNa súanymbotUn token vale menos que as respostas á solicitude402refund_insufficientUn fracaso non contabilizado coloca a taxa de volta no mesmo token. - Comproba iso.
GET /api/v1/l402/refundsco mesmo header volve{"sats": 237, "status": "open", "expires_at": "..."}. - Un token pode ser usado ata 60 veces por minuto.
- Mova-lo para unha nym. Poñémolo en Recibir un agasallo na aplicación Nymbot, ou chamando
POST /api/v1/l402/refunds/redeemCon unha Solicitude asinada e{"refund_token": "REFUND-...", "balance": "standard"}ou"pro"Os créditos enteiros van ao saldo (10 cotas cada unha no estándar, 100 no Pro); cotas que non fan que un crédito enteiro permaneza no token para as solicitudes de 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 } }