Treci la conținut
Înapoi la Nymbot

Baza de cunoștințe Dezvoltatorii

Balanță, top-up-uri și chei

Verificați ce aveți, suprapuneți Lightning, suprapuneți automat din portofelul dvs., vedeți ce costă fiecare cerere și gestionați cheile din cod.

Verifică echilibrul

Ambele balanțe și cât de mult din capacul acestei chei este folosit.

GET https://nymbot.ai/api/v1/credits/balance Aveți nevoie de o cheie de foc. POST Lucrează și pentru clienții care se așteaptă la asta.

balance este cele două solduri împreună în dolari la prețul curent Bitcoin, pentru instrumentele care se așteaptă la un singur număr (null Restul este în credite și rate, care este modul în care soldurile sunt de fapt păstrate. key Descrie cheia care a cerut. o cheie care a atins capul său poate verifica în continuare soldul.

răspuns

{
  "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"
  }
}
StatutulCând
401Cheia este lipsă, necunoscută, revocată sau expirată.

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

Metode de plată

Cum poți să te ridice, și limitele. fulgerul este singura metodă.

GET https://nymbot.ai/api/v1/topup/payment-methods Nu este nevoie de cheie.

Un top-up este de 10 până la 1.000.000 de rate; un top-up Pro trebuie să cumpere cel puțin un credit Pro, deci începe la 100 rate. bulk_bonus enumeră creditul suplimentar pe top-up-urile mai mari, același ca și în aplicație: 10%, 15% sau 20% mai mult pe top-up-urile standard de la 500, 1.000 sau 5.000 rate, și pe top-up-urile Pro de la 5.000, 10.000 sau 50.000 rate.

răspuns

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

Înălțarea deasupra fulgerului

Creează o factură Lightning care adaugă credit la nimm-ul la care aparține cheia. plătiți-l de la orice portofel Lightning, apoi Verifică Pentru a avea creditul adăugat.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Aveți nevoie de o cheie de foc.

câmpuluiTipulnecesarăDescrierea
amountNumărDaCât, în currencyUn număr întreg pentru pariuri.
currencyStringulnuSATS (în cazul în care defectul este USD sau BTCDolarurile sunt convertite la prețul curent al Bitcoin.
tierStringulnupro (defaultul) sau standard• la care se îndreaptă creditul.

Un credit standard este de 10 rate și un credit Pro de 100 rate, plus orice bonus în vrac; credits spune ce va adăuga această factură.

răspuns

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
StatutulCând
400O altă metodă în calea (unsupported_method, o monedă necunoscută (unsupported_currencyo sumă care lipseşte sau care este mai mică decât valoarea minimă (amount_too_small, mai mult de 1 000 000 de rate (amount_too_large) sau respinsă de portofelul Lightning (amount_out_of_range).
429Mai mult de 60 de facturi pentru acest nimm, sau 120 de la această adresă, într-o oră (rate_limit_exceededCu Retry-After).
502Nu se poate face nici o factură în acest moment (invoice_unavailableCu 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);

Verificarea unui top-up

Întreabă dacă factura a fost plătită și, odată ce are, adaugă creditul. creditedVerificarea din nou după aceea este sigură: creditul aterizează o dată, indiferent de câte ori cereți.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - are nevoie de o cheie de la nim care a făcut factura.

status este pending (nu a fost încă plătită) paid (Plătit, dar încă nu creditat; verificați din nou), credited (pe echilibrul tău) sau expired (nu se plătește la timp). câmpurile de echilibru sunt pentru nivelul pe care se ridică factura.

răspuns

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
StatutulCând
400ID-ul nu este ID-ul de 64 de caractere din apelul creat.
404Nu există nici o factură de către acel id pentru 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);

Vrei istorie

O linie pe cerere: ce a fost, ce model, câte jetoane și ce costă. Nu se păstrează nicio solicitare sau răspuns, deci nu se returnează nicio linie. Liniile sunt păstrate timp de 90 de zile, cel mai nou mai întâi. O cheie care a atins capul său poate citi încă istoria sa.

GET https://nymbot.ai/api/v1/queries/history — are nevoie de o cheie API, care să vadă propriile cereri, sau de o Cerere semnată din nimma ta, care vede fiecare cheie.

câmpuluiTipulnecesarăDescrierea
pageîntregului (query)nuDefault 1, la maxim 1000; o pagină mai mare este 400 invalid_value.
page_countîntregului (query)nuRoi pe pagină. Default 20, la maximum 100.
start_date
end_date
Întrebări (Query)nuISO 8601 date sau ore.
modelÎntrebări (Query)nuDoar acest model.
typeÎntrebări (Query)nuchat, responses, messages, image, video, speech, transcription sau embedding.
all_keysBooleană (de cerere)nuCu o cheie: true include fiecare cheie a aceluiași nym. false.
key_idÎntrebări (Query)nuCu o cerere semnată, sau cu all_keys=trueDoar această cheie.

răspuns

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

Semnarea cererilor de cont

Crearea, schimbarea și revocarea cheilor, rezumatul contului și top-up-urile automate nu iau o cheie API. Ei iau o semnătură de la nym-ul dvs., astfel încât o cheie scurtată poate cheltui până la capătul său, dar nu poate face niciodată o altă cheie sau își poate ridica capătul.

Aplicația face acest lucru pentru tine: totul în focului folie utilizează aceste puncte finale. Aveți nevoie doar de această secțiune pentru a gestiona cheile din propriul dvs. cod.

Semnătura este un eveniment Nostr de tipul 27235 (NIP-98), trimis baz64-codat în Authorization Header cu cuvântul Nostr În faţă :

Evenimentul

{
  "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 este adresa URL completă a solicitării, incluzând șirul de interogări, exact așa cum a fost trimis.
  • method Aceasta este metoda HTTP.
  • payload este SHA-256 al corpului de cerere brută, în hex. POST şi PATCH, iar corpul pe care îl trimiteți trebuie să fie byte pentru byte pe care l-ați hashat.
  • created_at trebuie să fie în termen de 60 de secunde de ceasul serverului.
  • Fiecare eveniment funcționează o singură dată, a GET incluse, astfel încât un header capturat să nu poată fi redat. Semnează un nou pentru fiecare solicitare. nonce etichetați cu o valoare aleatorie, astfel încât două solicitări semnate în aceeași secundă să fie încă diferite.
  • Corpul unei solicitări semnate poate fi de cel mult 64 KB, iar corpul necesită Content-Type: application/json.

Un eveniment lipsă se întoarce 401 missing_nostr_authunul care este slab format, slab semnat, prea vechi sau pentru un alt URL, metodă sau corp returnează invalid_nostr_auth, cu motivul în mesaj; un reutilizat se întoarce nostr_auth_replayedSemnătura este verificată înainte de citirea corpului, iar fiecare adresă poate eșua de 30 de ori pe minut (o adresă IPv6 contează ca întreaga sa /64); după aceea primește 429 cu Retry-After.

Browserele pot apela aceste puncte finale numai de pe site-urile proprii ale lui Nymbot (https://nymbot.ai, https://nymchat.app O pagină de pe orice alt site nu primește înapoi titluri CORS, deci nu poate citi ceea ce returnează. Originnu sunt afectate.

Cheia ta secretă

Semnarea are nevoie de cheia secretă a nimului tău (cheia secretă a nimului). nsec), care controlează totul: identitatea, istoria și soldul. Puneți-l doar într-un script pe o mașină în care aveți încredere, citiți-l din mediul înconjurător în loc să-l scrieți în fișier și preferați aplicația atunci când puteți.

Acești asistenți construiesc antetul. Exemplele ulterioare de pe această pagină le folosesc. Ei citesc cheia secretă în hex din NOSTR_SECRET_HEXCURL-ul utilizează o Nak instrument de linie de comandă, care utilizează o cheie nsec sau hex, și sha256sum În cazul 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");
}

Rezumatul contului

Ce afișează în partea de sus a foii API a aplicației: cheia publică, ambele echilibre, câte chei sunt active (nu au fost revocate sau expirate) și Top-up automată setări, sau null atunci când serverul nu le oferă.

GET https://nymbot.ai/api/v1/account Are nevoie de a Cerere semnată.

răspuns

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

Gestionarea cheilor

Punctele finale din spatele listei cheie a aplicației.Toate acestea au nevoie de un Cerere semnatăFiecare cheie este returnată în această formă, cu ori în ISO 8601 și sume în sats:

Obiectul cheie

{
  "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 este suficient pentru a recunoaște o cheie, dar nu pentru a o utiliza.

Listă cheie

GET https://nymbot.ai/api/v1/keys şi semnat.

câmpuluiTipulnecesarăDescrierea
include_revokedBooleană (de cerere)nuInclude chei revocate. implicit false.

răspuns

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

Fă o cheie

POST https://nymbot.ai/api/v1/keys semnat. întoarce 201.

câmpuluiTipulnecesarăDescrierea
nameStringulDa1 până la 40 de caractere, diferite de celelalte chei active (ignorarea cazului).
limit_satsîntreguluinuCapacitatea de cheltuieli în sats, cel puțin 1.Lăsați-o fără cap.
reset_periodStringulnudaily, weekly sau monthlyNevoie limit_satsLăsați-l pentru un capac care nu se resetă niciodată.
expire_atString sau integernuCând cheia încetează să mai funcționeze: o dată ISO 8601, sau milisecunde din 1970.

Răspunsul meu (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"
  }
}
StatutulCând
400Un nume lipsă sau prea lung; un nume deja utilizat (duplicate_name(Număr întreg de cel puțin 1; o perioadă de resetare fără cap; o expirare în trecut; un câmp necunoscut)unknown_parameter); sau 25 de chei active deja (too_many_keys).
429Mai mult de 60 de chei realizate de acest nimm, sau 120 de la această adresă, într-o oră (rate_limit_exceededCu 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);

Citeste o cheie

GET https://nymbot.ai/api/v1/keys/{id} şi semnat.

întoarcere {"data": {…}} cu obiectul cheie, sau 404 key_not_found dacă nici o cheie a ta nu are acel ID.

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

Schimbarea unei chei

PATCH https://nymbot.ai/api/v1/keys/{id} şi semnat.

Trimiteți oricare dintre name, limit_sats, reset_period şi expire_atcu aceleași reguli ca atunci când se face o cheie. null Nu se poate schimba nici o cheie revocată, nu se poate schimba o cheie revocată (400 key_revoked• Întoarcerea {"data": {…}} cu obiectul cheie actualizat.

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

Revocarea unei chei

DELETE https://nymbot.ai/api/v1/keys/{id} şi semnat.

oprește cheia imediat, pentru bine. Rămâne în lista cu revoked_at înfăptuit şi poate fi văzut cu include_revoked=trueRevocarea unei chei care este deja revocată răspunde în același mod. Doar cele mai noi 50 de chei revocate sunt păstrate; cele mai vechi sunt șterse atunci când o altă cheie este revocată.

răspuns

{ "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 în top-up

Conectați un portofel Lightning cu Nostr Wallet Connect și Nymbot ridică un echilibru pe cont propriu atunci când cheltuielile API sunt reduse. foaia API a aplicației are aceleași setări; acestea sunt punctele finale din spatele acesteia. Cerere semnată.

Cum funcționează: după ce o cerere de API este încărcată la soldul pe care ați ales să-l urmăriți, dacă soldul a scăzut sub pragul dvs., Nymbot face o factură pentru suma dvs. de top-up, vă cere portofelului să o plătească și adaugă creditul. Se ridică cel mult o dată la fiecare 5 minute pentru fiecare nim și sold, astfel încât o explozie de cereri nu poate drena portofelul. Cheltuielile în aplicații nu o declanșează. Timpul și dimensiunea ultimului top-up și ultima eroare sunt în setări; dacă o plată a trecut după o eroare, verificați factura cu Starea de top-up credite pe ea.

Înainte de a conecta un portofel

Nymbot îl stochează criptat și îl folosește doar pentru a-și plăti propriile facturi, dar face o conexiune doar pentru asta, cu un buget de cheltuieli în portofel, astfel încât cel mai mult ar putea plăti vreodată este un număr ales de tine. pay_invoice.

Conectarea unui portofel

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect şi semnat.

câmpuluiTipulnecesarăDescrierea
nwc_urlStringulDaStringul de conectare, începând cu nostr+walletconnect://Nymbot cere portofelul pentru get_info înainte de a-l salva, şi îl stochează criptat.
threshold_satsîntreguluiDaTop up atunci când soldul scade sub aceste multe rate. cel puțin 1.000.
topup_satsîntreguluiDaCât de mult să adăugați de fiecare dată. 1.000 la 1.000.000 de pariuri.
tierStringulnupro (defaultul) sau standard: echilibrul de a privi și de a ridica.

răspuns

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
StatutulCând
400Nu există nici o legătură de legătură (invalid_nwc_urlb) nu a primit niciun răspuns din partea comisarului (nwc_unreachable) sau a refuzat verificarea (nwc_rejected); conexiunea nu poate plăti facturi (nwc_missing_permission(sau o sumă în afara limitelor.
501Nu sunt activate automat top-up-urile pentru acest server (nwc_unavailableAcelași lucru este valabil și pentru celelalte două puncte finale.

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

Citește setările

GET https://nymbot.ai/api/v1/nwc-auto-topup şi semnat.

Returnează acelaşi obiect ca şi conectarea, cu connected: false şi celelalte câmpuri null atunci când nu este conectat niciun portofel. Stringul de conexiune în sine nu este niciodată returnat.

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

Desconexiune

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection şi semnat.

Șterge șirul de conexiuni stocate. Nu se mai fac top-up-uri. Pentru a fi sigur, puteți, de asemenea, să revocați conexiunea din portofel.

răspuns

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

Plătiți la cerere fără cheie

Punctele finale cu preț fix pot fi plătite pentru o singură solicitare la un moment dat în timpul Lightning, fără cheie, fără cont și fără sold: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations şi POST /embeddingsO cerere care poartă o cheie este facturată la sold ca de obicei; fluxul de plată începe numai atunci când nu este trimisă nicio cheie.

Nymbot vorbește două versiuni ale aceleiași idei, dintr-un backend: Lightning Labs' C402 (De asemenea, acceptat sub numele său vechi, LSAT) și proiectul IETF plată Schema de autentificare HTTP cu lightning Metoda şi charge Foloseşte ceea ce înţelege clientul tău.

Rulează propriul server

Plata fără cheie este activată numai atunci când API_L402_SECRET conține cel puțin 32 de byte aleatorii, cum ar fi hex (64 caractere) sau base64 (44). openssl rand -hex 32O valoare mai scurtă sau mai predictibilă oprește caracteristica și înregistrează de ce. API_L402_SECRET_PREVIOUS pentru o zi: credențialele, URL-urile de stare și provocările făcute sub aceasta continuă să funcționeze până când expiră.

Provocarea

Trimiteți solicitarea cu NU Authorization Dacă este valabil, nimic nu funcționează și obțineți 402 Payment Required cu o factură pentru exact ceea ce costă această cerere: același preț pe care un cheie ar plăti, convertit la 10 sats un credit standard sau 100 sats un credit Pro și rotunjit până la un întreg sat (cel puțin 1 sat, și cel puțin 0.05 credit minim). WWW-Authenticate Dificultăți pentru aceeași taxă:

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

Plăți request Parametrul este base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

O provocare este legată de punctul final, de Content-Type (tipul său media și, pentru multipart, limita sa) și la SHA-256 de corpul exact byte pe care le-ați trimis, și durează 15 minute. identică Încă o dată: același Content-Type şi aceleaşi byte JSON, sau pentru punctele terminale multiparţiale (/images/edits, /audio/transcriptions, /audio/translationsMajoritatea bibliotecilor HTTP aleg o nouă limită de fiecare dată când codifică un formular, deci codifică-l o dată și trimite acele byte de două ori.

Fiecare adresă poate solicita 30 de provocări pe minut (o adresă IPv6 contează ca întreaga sa /64). Solicitările a căror adresă nu este cunoscută împart o limită mai strictă de 10 pe minut, iar există o limită generală pentru provocările Nymbot pe toate adresele; o cerere respinsă înainte de o provocare este făcută (de exemplu, cu un corp care nu este valabil JSON) nu contează spre ea. 429 cu Retry-AfterPunctele terminale plătite apelate fără cheie sau credențiale se numără, de asemenea, spre limita generală de 120 de solicitări neautentificate pe minut pe adresă. Content-Type Nu este application/json (sau multipart/form-data pentru încărcături) este refuzat cu 415 Niciodată nu primește o factură.

Încorporările sunt prețuite pe baza unei estimări a jetoanelor din intrare, cu o marjă de 1,5 ori, deoarece numărul real este cunoscut numai după aceea. Întoarce tokenul.

Trimiterea plății

Plătiți factura cu orice portofel Lightning. Portofelul vă oferă imaginea prealabilă, 64 de caractere hexagonală. Apoi trimiteți aceeași cerere cu una dintre acestea:

schemăHeader
C402Authorization: L402 <macaroon>:<preimage> (LSAT Lucrează și el)
platăAuthorization: Payment <base64url JSON>În cazul în care JSON este {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

O solicitare plătită răspunde exact la fel ca cea făcută cu o cheie, cu excepția faptului că nymbot Obiectul nu are câmpuri de echilibru: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}Şi nu există nici X-Nymbot-Balance-Sats O cerere plătită cu schema de plată primește, de asemenea, o Payment-Receipt header (base64url JSON cu ID-ul de provocare, hash-ul de plată ca reference, status şi timestampSolicitările plătite nu sunt legate de niciun nim, deci nu apar în istoricul interogărilor.

StatutulCând
402 payment_already_usedFiecare plată plătește pentru o solicitare.Răspunsul este o nouă provocare pentru această solicitare, astfel încât un client care își cachează ultima credențială (ca lnget Da) pur și simplu pământ din nou.
402 payment_mismatchCredențialul a fost emis pentru un alt punct final, Content-Type O nouă provocare pentru această cerere vine cu ea; dacă plata a fost prea mică, ceea ce ați plătit se întoarce ca o Întoarce tokenul (refund_token şi refund_sats în interiorul corpului).
402 payment_expiredAu trecut mai mult de 15 minute de la provocare.O nouă provocare vine cu ea.Dacă imaginea de pornire arată că ați plătit, ceea ce ați plătit se întoarce ca o Întoarce tokenul (refund_token şi refund_sats în corp), o dată; apoi credenţialul este utilizat.
401 invalid_preimagePreimaginea nu se adaugă la hash-ul de plată al facturii.
401 invalid_payment_credentialCredențialul este defectuos, a fost schimbat după ce Nymbot l-a emis sau numează un hash de plată pentru care Nymbot nu a emis niciodată o factură.
429 rate_limit_exceededMai mult de 30 de credențiale sau chei care nu au reușit să verifice au venit de la această adresă într-un minut, sau un token de rambursare a fost trimis de mai mult de 60 de ori într-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"));

Clienții au construit mppx cu o metodă Lightning se ocupă de provocarea de plată ei înșiși; indicați-i la punctul final și lăsați-i să plătească.

Videoclipuri

O plătită POST /videos răspunsuri 202 ca o cheie, plus a status_url: GET Este semnat și funcționează timp de 24 de ore, atâta timp cât locul de muncă este păstrat.

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

Păstrează refund_token din acest răspuns: este afișat numai aici. Este gol în timp ce videoclipulGET /api/v1/l402/refunds răspunsuri "status": "pending"); în cazul în care renderul eșuează fără facturare, plata aterizează pe acesta. refund_sats pentru un loc de muncă rambursat, dar niciodată tokenul, astfel încât partajarea URL-ului de stare nu împărtășește rambursarea.

Rambursări

Dacă o cerere plătită eșuează și furnizorul a facturat Nymbot pentru încercare, plata este păstrată și eroarea spune așa, cu charged_sats, exact ca pentru o cerere cheie. Dacă aceasta eșuează fără a fi facturată, eroarea Întoarce tokenul Merită ce ai plătit:

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

Piesele neutilizate se întorc în același mod: dacă ați cerut două fotografii și una nu a fost facturată, răspunsul de succes nymbot obiectul poartă un token de rambursare pentru unul lipsă; o transcriere a cărei lungime nu putea fi citită în față este prețuită pentru cea mai lungă durată posibilă a fișierului (nu mai mult de 30 de minute), iar diferența față de lungimea reală revine ca un token de rambursare; dacă se dovedește a fi mai lungă de 30 de minute, este refuzată cu 413 Încorporările returnează ceea ce estimarea a reținut. Un videoclip eşuat rambursează tokenul pe care a returnat-o depunerea.

Un token de rambursare este un cod aleatoriu de 256 de biți. Nymbot stochează numai hash-ul său și expiră după 30 de zile.

  • plătească cu ea. Trimiteți Authorization: Bearer REFUND-… În cazul în care, în conformitate cu prevederile art. 4 din Codul de procedură civilă, se aplică o astfel de măsură, se aplică următoarele condiţii: (refund_token_sats În nymbot Un token care valorează mai puțin decât răspunsul la cerere 402 refund_insufficientUn eșec necontat pune sats-ul înapoi pe același token.
  • Verifică acest lucru. GET /api/v1/l402/refunds Întoarce același header {"sats": 237, "status": "open", "expires_at": "..."}.
  • Un token poate fi folosit de cel mult 60 de ori pe minut.
  • Mutați-l la un nym. Puneți-l în Răscumpără un cadou în aplicația Nymbot, sau apel POST /api/v1/l402/refunds/redeem Cu a Cerere semnată şi {"refund_token": "REFUND-...", "balance": "standard"} (sau "pro"Creditele întregi merg la sold (10 rate fiecare pe standard, 100 pe Pro); rate care nu fac un credit întreg să rămână pe token pentru cererile 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"

răspuns

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