base de conocimientos desarrolladores
Balance, top-ups y llaves
Compruebe lo que tiene, sobreponerse a Lightning, sobreponerse automáticamente desde su propia cartera, ver cuánto cuesta cada solicitud y gestionar las claves de código.
Esta página está traducida automáticamente para mayor comodidad. La versión original en inglés es la que se aplica.
Comprobar el equilibrio
Ambos de sus saldos, y cuánto de la capa de esta clave se utiliza.
GET https://nymbot.ai/api/v1/credits/balance Necesita una llave de fuego. POST También para los clientes que lo esperan.
balance es los dos saldos juntos en dólares al precio actual de Bitcoin, para herramientas que esperan un único número (null El resto está en créditos y sats, que es como los saldos se mantienen realmente. key Una clave que ha alcanzado su límite todavía puede comprobar el saldo.
Respuesta
{
"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"
}
}
| Estatus | Cuando |
|---|---|
401 | La clave está desaparecida, desconocida, revocada o expirada. |
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 pago
Cómo se puede superar, y los límites. Lightning es el único método.
GET https://nymbot.ai/api/v1/topup/payment-methods No es necesaria la llave.
Un top-up es de 10 a 1.000.000 sats; un top-up Pro tiene que comprar al menos un crédito Pro, por lo que comienza en 100 sats. bulk_bonus enumera el crédito adicional en top-ups más grandes, lo mismo que en la aplicación: 10%, 15% o 20% más en top-ups estándar de 500, 1.000 o 5.000 sats, y en top-ups Pro de 5.000, 10.000 o 50.000 sats.
Respuesta
{
"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);
Subiendo por encima del rayo
Crea una factura de relámpago que agrega crédito a la nube a la que pertenece la clave. Comprobarlo Para tener el crédito añadido.
POST https://nymbot.ai/api/v1/topup/create/btc-lightning Necesita una llave de fuego.
| Campo | Tipo | Requiere | Descripción |
|---|---|---|---|
amount | número | Sí | ¿Cuánto, en currencyUn número entero para la apuesta. |
currency | String | No | SATS (en el caso del defecto), USD o BTCLos dólares se convierten al precio actual de Bitcoin. |
tier | String | No | pro (el defecto) o standard: a qué balance va el crédito. |
Un crédito estándar es 10 sats y un crédito Pro 100 sats, más cualquier bonificación en volumen;
credits dice lo que esta factura añadirá.
Respuesta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"payment_request": "lnbc100u1p5...",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": "2026-09-30T09:27:00Z",
"status": "pending"
}
| Estatus | Cuando |
|---|---|
400 | Un nuevo método en el camino (unsupported_method), una moneda desconocida (unsupported_currency) o nivel, una cantidad faltante, o una cantidad por debajo del mínimo (amount_too_small), por encima de 1.000.000 de tarifas (amount_too_large) o rechazado por la cartera Lightning (amount_out_of_range). |
429 | Más de 60 facturas para este nym, o 120 desde esta dirección, en una hora (rate_limit_exceeded, con Retry-After). |
502 | No se puede hacer factura en este 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);
Comprobar el top-up
Pregunta si la factura ha sido pagada y, una vez que la tiene, añade el crédito. creditedVerificar de nuevo después es seguro: el crédito aterriza una vez, no importa cuántas veces lo pidas.
GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - Necesita una llave de la nim que hizo la factura.
status es pending (Aún no se ha pagado) paid (Pago, pero aún no acreditado; compruebe de nuevo), credited (en su balance) o expired (no pagado a tiempo).Los campos de balance son para el nivel en el que la factura se eleva.
Respuesta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"status": "credited",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": null,
"balance_credits": 523.33,
"balance_sats": 52333
}
| Estatus | Cuando |
|---|---|
400 | El id no es el id de 64 caracteres de la llamada creada. |
404 | No hay factura por ese id para tu 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);
Queremos historia
Una fila por solicitud: qué era, qué modelo, cuántos tokens y cuánto costó. No se conservan prompts ni respuestas, por lo que no se devuelve ninguno. Las filas se conservan durante 90 días, lo más reciente primero. Una clave que ha alcanzado su límite todavía puede leer su historia.
GET https://nymbot.ai/api/v1/queries/history — necesita una clave de API, que ve sus propias solicitudes, o una Solicitud firmada de tu ninja, que ve cada llave.
| Campo | Tipo | Requiere | Descripción |
|---|---|---|---|
page | Es decir, el integer (query | No | Por defecto 1, con un máximo de 1000; una página más alta es 400 invalid_value. |
page_count | Es decir, el integer (query | No | Rodas por página. 20 por defecto, con un máximo de 100. |
start_dateend_date | Conexión (Query) | No | ISO 8601 Datas y horas. |
model | Conexión (Query) | No | Sólo este modelo. |
type | Conexión (Query) | No | chat, responses, messages, image, video, speech, transcription o embedding. |
all_keys | Boolean (en español) | No | Con una llave: true incluye todas las claves del mismo nym. Default false. |
key_id | Conexión (Query) | No | Con una solicitud firmada, o con all_keys=trueSólo esta llave. |
Respuesta
{
"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);
Solicitud de firma de cuenta
La creación, el cambio y la revocación de claves, el resumen de la cuenta y los top-ups automáticos no toman una clave de API. Ellos toman una firma de su nym, por lo que una clave que se ha filtrado puede gastar hasta su límite, pero nunca puede hacer otra clave o elevar su propio límite.
La aplicación hace esto por usted: todo en su Fuego Sheet utiliza estos puntos finales. sólo necesita esta sección para administrar las claves de su propio código.
La firma es un evento Nostr del tipo 27235 (NIP-98), enviado base64-codificado en el
Authorization Header con la palabra Nostr En el frente:
El 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": "..."
}
ues la URL completa de la solicitud, la cadena de consulta incluida, exactamente como fue enviada.methodEs el método HTTP.payloades el SHA-256 del cuerpo de la solicitud cruda, en hex.POSTyPATCH, y el cuerpo que envía debe ser byte por byte el que ha hashado.created_atDebe estar dentro de los 60 segundos del reloj del servidor.- Cada evento funciona una vez, a
GETincluido, por lo que un encabezado capturado no puede ser reproducido. Asine un nuevo para cada solicitud.nonceetiqueta con un valor aleatorio para que dos solicitudes firmadas en el mismo segundo sigan siendo diferentes. - El cuerpo de una solicitud firmada puede ser de un máximo de 64 KB, y un cuerpo necesita
Content-Type: application/json.
Un evento perdido regresa 401 missing_nostr_auth; uno que está mal formado, mal firmado, demasiado viejo, o para una URL, método o cuerpo diferente devuelve
invalid_nostr_auth, con la razón en el mensaje; un reutilizado uno vuelve
nostr_auth_replayedLa firma se verifica antes de que se lea el cuerpo, y cada dirección puede fallarlo 30 veces por minuto (una dirección IPv6 cuenta como su totalidad /64); después de eso se obtiene 429 con
Retry-After.
Los navegadores pueden llamar a estos puntos finales sólo desde los sitios propios de Nymbot (https://nymbot.ai,
https://nymchat.app Una página en cualquier otro sitio no recibe encabezados CORS de vuelta, por lo que no puede leer lo que devuelven.
OriginNo están afectados.
La firma necesita la llave secreta de tu nim (la nsec), que controla todo: su identidad, su historial y su balance. Solo coloque en un script en una máquina en la que confíe, lea desde el entorno en lugar de escribirlo en el archivo, y prefiere la aplicación cuando pueda.
Estos ayudantes construyen el encabezado. Los ejemplos posteriores en esta página los usan. NOSTR_SECRET_HEX; el cURL uno utiliza el
Naco herramienta de línea de comandos, que toma una clave nsec o hex, y 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");
}
El resumen de la cuenta
Lo que muestra la hoja de la API de la aplicación en la parte superior: su clave pública, ambos saldos, cuántas claves están activas (no revocadas o expiradas), y la Top-up automático establecimientos, o
null cuando el servidor no los ofrece.
GET https://nymbot.ai/api/v1/account Necesito a Solicitud firmada.
Respuesta
{
"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ón de claves
Los puntos finales detrás de la lista de claves de la aplicación. Todos ellos necesitan una Solicitud firmadaCada clave se devuelve en este formulario, con tiempos en ISO 8601 y cantidades en sats:
Objeto 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 Es suficiente para reconocer una llave pero no para utilizarla.La misma llave sólo se devuelve una vez, cuando se hace.
Listado de claves
GET https://nymbot.ai/api/v1/keys y firmado.
| Campo | Tipo | Requiere | Descripción |
|---|---|---|---|
include_revoked | Boolean (en español) | No | Incluye las claves revocadas. false. |
Respuesta
{ "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);
Hacer una llave
POST https://nymbot.ai/api/v1/keys Regreso - Regreso 201.
| Campo | Tipo | Requiere | Descripción |
|---|---|---|---|
name | String | Sí | 1 a 40 caracteres, diferentes de sus otras claves activas (ignorar el caso). |
limit_sats | Entierro | No | El límite de gasto en sats, al menos 1. |
reset_period | String | No | daily, weekly o monthlyNecesidades limit_satsDejarlo fuera para una tapa que nunca se restaura. |
expire_at | String o integer | No | Cuando la clave deja de funcionar: un tiempo ISO 8601, o milisegundos desde 1970. |
La respuesta (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"
}
}
| Estatus | Cuando |
|---|---|
400 | Un nombre perdido o demasiado largo; un nombre ya en uso (duplicate_name); un cap que no es un número entero de al menos 1; un período de restauración sin cap; una expiración en el pasado; un campo desconocido (unknown_parameter); o 25 claves activas ya (too_many_keys). |
429 | Más de 60 llaves hechas por este nim, o 120 desde esta dirección, en una 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);
Leer una clave
GET https://nymbot.ai/api/v1/keys/{id} y firmado.
Regreso {"data": {…}} con el objeto clave, o 404
key_not_found si ninguna de tus claves tiene 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 una llave
PATCH https://nymbot.ai/api/v1/keys/{id} y firmado.
Envíe cualquier name, limit_sats, reset_period y
expire_at, con las mismas reglas que cuando se hace una llave. null Cambiar el período de restauración inicia un nuevo período a cero. (400 key_revoked) Regreso
{"data": {…}} con el objeto 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);
Revocación de una clave
DELETE https://nymbot.ai/api/v1/keys/{id} y firmado.
Detiene la llave de inmediato, para bien. Se mantiene en la lista con revoked_at y se puede ver con include_revoked=trueRevocar una clave que ya ha sido revocada responde de la misma manera. Sólo se conservan las últimas 50 claves revocadas; las más antiguas se borran cuando se revoca otra clave.
Respuesta
{ "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 España
Conecte una cartera de Lightning con Nostr Wallet Connect y Nymbot sube un saldo por sí solo cuando el gasto de API lo ejecuta bajo. la hoja de API de la aplicación tiene las mismas configuraciones; estos son los puntos finales detrás de ella. Solicitud firmada.
Cómo funciona: después de que una solicitud de API se cargue en el saldo que eligió para ver, si ese saldo ha caído por debajo de su umbral, Nymbot hace una factura para su cantidad superior, le pide a su cartera que la pague, y añade el crédito. Se eleva a lo más una vez cada 5 minutos para cada nim y saldo, por lo que una explosión de solicitudes no puede drenar la cartera. El gasto en las aplicaciones no lo desencadena. El tiempo y el tamaño del último top-up, y el último error, están en las configuraciones; si un pago pasó después de un error, comprueba su factura con El estado top-up que lo acrediten.
Nymbot lo almacena encriptado y sólo lo utiliza para pagar sus propias facturas de arriba, pero haga una conexión sólo para esto, con un presupuesto de gasto en su cartera, por lo que lo más que podría pagar es un número elegido por usted. pay_invoice.
Conectar una cartera
POST https://nymbot.ai/api/v1/nwc-auto-topup/connect y firmado.
| Campo | Tipo | Requiere | Descripción |
|---|---|---|---|
nwc_url | String | Sí | La cadena de conexión, que comienza nostr+walletconnect://Nymbot pide la cartera para get_info antes de guardarlo, y lo almacena encriptado. |
threshold_sats | Entierro | Sí | Top up cuando el saldo cae por debajo de estos muchos sats. al menos 1.000. |
topup_sats | Entierro | Sí | Cuánto añadir cada vez. 1.000 a 1.000.000 sats. |
tier | String | No | pro (el defecto) o standard: el equilibrio para observar y top up. |
Respuesta
{
"data": {
"connected": true,
"threshold_sats": 5000,
"topup_sats": 20000,
"tier": "pro",
"last_topup_at": null,
"last_topup_sats": null,
"last_error": null
}
}
| Estatus | Cuando |
|---|---|
400 | No es una línea de conexión (invalid_nwc_url); la cartera no respondió sobre su relay (nwc_unreachable) o rechazó el cheque (nwc_rejected); la conexión no puede pagar facturas (nwc_missing_permission(o una cantidad fuera de los límites. |
501 | No se activan las top-ups automáticas para este servidor (nwc_unavailableLo mismo ocurre con los otros dos puntos finales. |
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());
Leer las configuraciones
GET https://nymbot.ai/api/v1/nwc-auto-topup y firmado.
Devuelve el mismo objeto que conectar, con connected: false Y los otros campos
null cuando no hay cartera conectada. La cadena de conexión en sí nunca se devuelve.
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 y firmado.
Elimina la cadena de conexión almacenada. No se realizan más top-ups. Para estar seguro, también puede revocar la conexión en su cartera.
Respuesta
{ "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());
Pago por solicitud sin llave
Los puntos finales de precio fijo se pueden pagar por una solicitud a la vez a través de Lightning, sin llave, sin cuenta y sin saldo: POST /images/generations, POST /images/edits,
POST /videos, POST /audio/speech, POST /audio/transcriptions,
POST /audio/translations y POST /embeddingsChat, Respuestas y Mensajes siempre necesitan una clave.Una solicitud que lleva una clave se factura al saldo como de costumbre; el flujo de pago sólo comienza cuando no se envía ninguna clave.
Nymbot habla de dos versiones de la misma idea, desde un backend: Lightning Labs
L402 (También aceptado bajo su antiguo nombre, LSAT) y el proyecto IETF
Pago Esquema de autenticación HTTP con lightning Método y
charge Utilice lo que su cliente entienda.
Pagar sin una llave está en solo cuando API_L402_SECRET contiene al menos 32 bytes aleatorios, como hex (64 caracteres) o base64 (44). openssl rand -hex 32Un valor más corto o estimable apaga la función y registra por qué. Para girarla, desplaza el valor antiguo a API_L402_SECRET_PREVIOUS durante un día: las credenciales, las URLs de estado y los desafíos realizados bajo ella continúan funcionando hasta que expiran.
El desafío
Enviar la solicitud con no Authorization si es válido, nada se ejecuta, y se obtiene 402 Payment Required con una factura para exactamente lo que esa solicitud cuesta: el mismo precio que una clave pagaría, convertido en 10 sats un crédito estándar o 100 sats un crédito Pro y redondeado hasta un sat completo (al menos 1 sat, y al menos el mínimo de crédito 0.05). WWW-Authenticate Desafíos para la misma 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 pago request El parámetro es base64url JSON:
{"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.
Un desafío está vinculado al punto final, al Content-Type (su tipo de soporte y, para multipart, su límite) y al SHA-256 de los bytes de cuerpo exactos que envió, y dura 15 minutos. identidad Solicitar otra vez: lo mismo
Content-Type y los mismos bytes JSON, o para los puntos finales multipartes (/images/edits, /audio/transcriptions, /audio/translationsLa mayoría de las bibliotecas HTTP eligen un nuevo límite cada vez que codifican un formulario, por lo que codifican una vez y envían esos bytes dos veces.
Cada dirección puede pedir 30 desafíos por minuto (una dirección IPv6 cuenta como su totalidad /64). Las solicitudes cuya dirección no es conocida comparten un 10 más estricto por minuto, y hay un límite general en los desafíos Nymbot problemas en todas las direcciones; una solicitud rechazada antes de que se haga un desafío (por ejemplo, con un cuerpo que no es válido JSON) no cuenta hacia ella. 429 con Retry-AfterLos puntos finales pagados llamados sin una clave o credencial también cuentan hacia el límite general de 120 solicitudes no autenticadas por minuto por dirección. Content-Type No es application/json (o el
multipart/form-data para las subidas) se rechaza con 415 Nunca recibe una factura.
Las incorporaciones se valoran a partir de una estimación de los tokens en la entrada, con un margen de 1,5 veces, ya que el número real sólo se conoce después. Reembolsar Token.
Enviar el pago
Paga la factura con cualquier billetera Lightning. La billetera te da la preimagen, 64 caracteres hexadecimales. Luego envía la misma solicitud con uno de estos:
| El esquema | Header |
|---|---|
| L402 | Authorization: L402 <macaroon>:<preimage> (LSAT también funciona) |
| Pago | Authorization: Payment <base64url JSON>Dónde está el JSON {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}} |
Una solicitud de pago responde exactamente como una solicitud realizada con una llave, excepto que la nymbot
El objeto no tiene campos de equilibrio: {"payment": "l402", "tier": "pro", "paid_sats": 237,
"charged_sats": 237}Y hay que no X-Nymbot-Balance-Sats Una solicitud pagada con el esquema de pago también recibe un Payment-Receipt header (base64url JSON con el ID de desafío, el hash de pago como reference, status y
timestampLas solicitudes pagadas no están vinculadas a ningún nim, por lo que no aparecen en el historial de consultas.
| Estatus | Cuando |
|---|---|
402 payment_already_used | Cada pago paga por una solicitud.La respuesta es un nuevo desafío para esta solicitud, por lo que un cliente que cacha su última credencial (como lnget Y es que, simplemente, vuelve a nacer. |
402 payment_mismatch | La credencial se emitió para otro punto final, Content-Type o cuerpo, o paga menos de lo que esta solicitud ahora cuesta. Un nuevo desafío para esta solicitud viene con él; si el pago fue demasiado pequeño, lo que pagó vuelve como un Reembolsar Token (refund_token y refund_sats en el cuerpo). |
402 payment_expired | Más de 15 minutos han pasado desde el reto. Un nuevo desafío viene con él. Si la preimagen muestra que has pagado, lo que has pagado vuelve como un Reembolsar Token (refund_token y refund_sats en el cuerpo), una vez; la credencial se usa después. |
401 invalid_preimage | La preimagen no hash al hash de pago de la factura. |
401 invalid_payment_credential | La credencial está mal formada, fue cambiada después de que Nymbot la emitió, o nombres de un hash de pago que Nymbot nunca emitió una factura. |
429 rate_limit_exceeded | Más de 30 credenciales o claves que no pudieron verificarse llegaron de esta dirección en un minuto, o un token de reembolso fue enviado más de 60 veces en un 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"));
Clientes construidos en mppx con un método Lightning manejan el desafío de pago ellos mismos; señalarlos en el punto final y dejar que paguen.
Videos
El pago POST /videos Respuestas 202 como una llave, más a
status_url: GET Se firma y funciona durante 24 horas, siempre que se mantenga el trabajo.
{
"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..."
}
}
Mantenga el refund_token de esta respuesta: sólo se muestra aquí. Está vacío mientras el videoGET /api/v1/l402/refunds Respuestas "status": "pending"); si el rendimiento falla sin facturar, el pago aterriza en él. La URL de estado muestra
refund_sats para un trabajo reembolsado pero nunca el token, por lo que compartir la URL de estado no comparte el reembolso.
reembolsos
Si una solicitud pagada falla y el proveedor factura a Nymbot por el intento, el pago se mantiene y el error dice eso, con charged_sats, exactamente como para una solicitud clave. Si falla sin ser facturado, el error lleva un Reembolsar Token Vale lo que pagaste:
{
"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"
}
}
Las piezas no utilizadas se devuelven de la misma manera: si pidió dos fotos y uno falló sin facturar, la respuesta de éxito nymbot el objeto lleva un token de reembolso para el que falta; una transcripción cuya longitud no podía ser leída de antemano se precia por lo más largo que el archivo pudiera ser (nunca más de 30 minutos), y la diferencia a la longitud real se devuelve como un token de reembolso; si resulta ser más largo de 30 minutos, se rechaza con 413
Los embeddings devuelven lo que la estimación retiene.Un vídeo fallido devuelve el token de su presentación devuelto.
Un token de reembolso es un código aleatorio de 256 bits. Nymbot solo almacena su hash, y expira después de 30 días.
- Paga con ella. Envío
Authorization: Bearer REFUND-…en cualquiera de los puntos finales anteriores (un SDK OpenAI lo toma como su clave API).El precio sale del token y lo que queda queda en él (refund_token_satsEn lanymbotUn token vale menos que las respuestas a la solicitud402refund_insufficientUn fracaso no contable pone la sats de vuelta en el mismo token. - Compruebe lo
GET /api/v1/l402/refundscon el mismo header vuelve{"sats": 237, "status": "open", "expires_at": "..."}. - Un token puede ser usado hasta 60 veces por minuto.
- Moverlo a un nym. Ponerlo en Recibir un regalo en la aplicación Nymbot, o llamando
POST /api/v1/l402/refunds/redeemcon a Solicitud firmada y{"refund_token": "REFUND-...", "balance": "standard"}(o el"pro"Los créditos enteros van al saldo (10 centavos cada uno en estándar, 100 en Pro); centavos que no hacen que un crédito entero permanezca en el token para las 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"
Respuesta
{ "data": { "credited": 23, "tier": "standard", "balance_credits": 123, "remaining_sats": 7 } }