Hoppa till innehållet
Tillbaka till Nymbot

Kunskapsbas Utvecklare

Balans, toppar och nycklar

Kontrollera vad du har, topp upp över Lightning, topp upp automatiskt från din egen plånbok, se vad varje förfrågan kostar och hantera nycklar från kod.

Kontrollera balansen

Både dina balansräkningar, och hur mycket av den här nyckelns lock används.

GET https://nymbot.ai/api/v1/credits/balance Behöver en API nyckel. POST Det fungerar också, för kunder som förväntar sig det.

balance är de två balanserna tillsammans i dollar vid det nuvarande Bitcoin-priset, för verktyg som förväntar sig ett enda nummer (null Resten är i krediter och satser, vilket är hur balanserna faktiskt hålls. key En nyckel som har nått sin cap kan fortfarande kontrollera balansen.

svarar

{
  "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"
  }
}
statusenNär
401Nyckeln saknas, är okänd, återkallad eller har gått ut.

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

Betalningsmetoder

Hur du kan topp upp, och gränserna. blixtnedslag är den enda metoden.

GET https://nymbot.ai/api/v1/topup/payment-methods Ingen nyckel behövs.

En top-up är 10 till 1 000 000 satsar; en Pro-top-up måste köpa minst en Pro-kredit, så det börjar vid 100 satsar. bulk_bonus listar den extra krediten på större toppar, samma som i appen: 10%, 15% eller 20% mer på standard toppar från 500, 1.000 eller 5.000 satser, och på Pro toppar från 5.000, 10.000 eller 50.000 satser.

svarar

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

Hoppa upp över blixten

Skapar en Lightning-faktura som lägger till kredit till nym nyckeln tillhör. Kontrollera det för att få krediten tillägg.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Behöver en API nyckel.

fältetTyp avnödvändigtBeskrivning
amountAntaletJaHur mycket, i currencyEtt helt nummer för sats.
currencySträngarNejSATS (Det är en defekt) USD eller BTCDollar konverteras till det nuvarande Bitcoin-priset.
tierSträngarNejpro (default) eller standard: vilken balans krediten går till.

En standard kredit är 10 sats och en Pro kredit 100 sats, plus någon bulk bonus; credits säger vad denna faktura kommer att lägga till.

svarar

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
statusenNär
400En annan metod i vägen (unsupported_method, en okänd valuta (unsupported_currency) eller en nivå, ett saknat belopp, eller ett belopp under minimibeloppet (amount_too_small) över 1 000 000 satser (amount_too_large) eller avvisas av Lightning Wallet (amount_out_of_range).
429Mer än 60 fakturor för denna nym, eller 120 från denna adress, på en timme (rate_limit_exceeded, med Retry-After).
502Inget faktura kan göras just nu (invoice_unavailable, med 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);

Kolla upp en topp

Frågar om fakturan har betalats och, när den har, lägger till krediten. Kontrollera är vad krediter det, så efter att ha betalat, kontrollera tills statusen är creditedKontrollera igen efteråt är säkert: krediten landar en gång, oavsett hur många gånger du frågar.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - behöver en nyckel från nym som gjorde fakturan.

status är pending (Jag har inte betalat ännu) paid (betalt men ännu inte krediterat; kolla igen), credited (på din balans) eller expired (inte betalt i tid). Balansfälten är för den nivå som fakturan toppar upp.

svarar

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
statusenNär
400ID:n är inte 64-karaktärs-ID:n från det skapade samtalet.
404Ingen faktura av det id för din 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);

Vill ha historia

En rad per begäran: vad det var, vilken modell, hur många tokens och vad det kostade. Inga uppmaningar eller svar hålls, så ingen returneras. Rader hålls i 90 dagar, nyaste först. En nyckel som har nått sin topp kan fortfarande läsa sin historia.

GET https://nymbot.ai/api/v1/queries/history — behöver en API-nyckel, som ser sina egna förfrågningar, eller en Signerad begäran från din nym, som ser varje nyckel.

fältetTyp avnödvändigtBeskrivning
pageInnehåller inte QueryNejStandard 1, högst 1000; en högre sida är 400 invalid_value.
page_countInnehåller inte QueryNejRader per sida. Standard 20, högst 100.
start_date
end_date
sträng (förfrågan)NejISO 8601 datum och tider.
modelsträng (förfrågan)NejEndast denna modell.
typesträng (förfrågan)Nejchat, responses, messages, image, video, speech, transcription eller embedding.
all_keysBooleanska (förfrågan)NejMed en nyckel: true innehåller varje nyckel av samma nym. false.
key_idsträng (förfrågan)Nejmed en undertecknad ansökan, eller med all_keys=trueBara den här nyckeln.

svarar

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

Signering av kontoförfrågningar

Skapa, ändra och återkalla nycklar, kontosammanfattningen och automatiska toppar tar inte en API-nyckel.De tar en signatur från din nym, så en läckt nyckel kan spendera upp till sin cap men kan aldrig göra en annan nyckel eller höja sin egen cap.

Appen gör detta för dig: allt i sin elden Du behöver bara det här avsnittet för att hantera nycklar från din egen kod.

Signaturen är en Nostr-händelse av typen 27235 (NIP-98), skickad bas64-kodad i Authorization header med ordet Nostr På framsidan:

händelsen

{
  "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 är den fullständiga URL-adressen för begäran, fråga sträng ingår, exakt som skickas.
  • method Det är HTTP metoden.
  • payload är SHA-256 av den råa begäran kroppen, i hex. POST och PATCH, och den kropp du skickar måste vara byte för byte den du hashade.
  • created_at måste vara inom 60 sekunder av serverns klocka.
  • Varje händelse fungerar en gång, a GET ingår, så att en fångad rubrik inte kan spelas upp igen. Signera en ny för varje begäran. nonce tagga med ett slumpmässigt värde så att två förfrågningar som undertecknats i samma sekund fortfarande skiljer sig åt.
  • Kroppen för en signerad begäran kan vara högst 64 KB, och en kropp behöver Content-Type: application/json.

En saknad händelse återkommer 401 missing_nostr_authen som är dåligt bildad, dåligt signerad, för gammal, eller för en annan URL, metod eller kropp returnerar invalid_nostr_auth, med orsaken i meddelandet; en återanvänd en returnerar nostr_auth_replayedEn API-nyckel som skickas till dessa slutpunkter nekas. signaturen kontrolleras innan kroppen läses, och varje adress kan misslyckas med det 30 gånger per minut (en IPv6-adress räknas som sin helhet /64); därefter får den 429 med Retry-After.

Webbläsare kan endast ringa dessa slutpunkter från Nymbots egna webbplatser (https://nymbot.ai, https://nymchat.app En sida på någon annan webbplats får inga CORS-rubriker tillbaka, så den kan inte läsa vad de returnerar. OriginDe påverkas inte.

Din hemliga nyckel

Signering behöver din nym's hemliga nyckel (den nsec), som kontrollerar allt: din identitet, din historia och din balans. Endast sätta det i ett skript på en maskin du litar på, läsa det från miljön istället för att skriva det i filen, och föredra appen när du kan.

Dessa hjälpare bygger rubriken. De senare exemplen på den här sidan använder dem. De läser den hemliga nyckeln i hex från NOSTR_SECRET_HEX; cURL en använder den Naken kommandoradsverktyg, som tar en nsec eller hexnyckel, och sha256sum För 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");
}

Konto sammanfattning

Vad appens API-ark visar högst upp: din offentliga nyckel, båda balanserar, hur många nycklar som är aktiva (inte återkallade eller utgångna) och Automatiska toppar inställningar, eller null när servern inte erbjuder dem.

GET https://nymbot.ai/api/v1/account Behöver a Signerad begäran.

svarar

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

Hantera nycklar

Slutpunkterna bakom appens nyckellista. Alla behöver en Signerad begäranVarje nyckel returneras i följande form, med tider i ISO 8601 och belopp i sats:

Nyckelobjekt

{
  "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 Det räcker att känna igen en nyckel men inte att använda den. Nyckeln returneras endast en gång när den är gjord.

Lista nycklar

GET https://nymbot.ai/api/v1/keys och undertecknat .

fältetTyp avnödvändigtBeskrivning
include_revokedBooleanska (förfrågan)NejInkludera återkallade nycklar. Standard false.

svarar

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

Att göra en nyckel

POST https://nymbot.ai/api/v1/keys signerat. återvänder 201.

fältetTyp avnödvändigtBeskrivning
nameSträngarJa1 till 40 tecken, som skiljer sig från dina andra aktiva nycklar (om du ignorerar fallet).
limit_satsintegerNejUtgiftskapaciteten i sats, åtminstone 1.Lämna ut den för ingen cap.
reset_periodSträngarNejdaily, weekly eller monthlyBehöver limit_satsLämna det för en lock som aldrig återställs.
expire_atString eller integerNejNär nyckeln slutar fungera: en ISO 8601-tid, eller millisekunder sedan 1970.

Svar på frågorna (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"
  }
}
statusenNär
400Ett saknat eller för långt namn; ett namn som redan används (duplicate_name); en cap som inte är ett heltal på minst 1; en återställningsperiod utan cap; en utgång i det förflutna; ett okänt fält (unknown_parameter); eller 25 aktiva nycklar redan (too_many_keys).
429Mer än 60 nycklar gjorda av denna nym, eller 120 från denna adress, på en timme (rate_limit_exceeded, med 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);

Läs en nyckel

GET https://nymbot.ai/api/v1/keys/{id} och undertecknat .

återvänder {"data": {…}} med nyckelobjektet, eller 404 key_not_found om ingen av dina nycklar har detta 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);

Byta nyckel

PATCH https://nymbot.ai/api/v1/keys/{id} och undertecknat .

Skicka någon av name, limit_sats, reset_period och expire_atmed samma regler som när du gör en nyckel. null rensar ett fält: ingen kap, ingen återställning, ingen utgång. Ändra återställningsperioden startar en ny period vid noll. En återkallad nyckel kan inte ändras (400 key_revoked) återvänder {"data": {…}} med det uppdaterade nyckelobjektet.

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

Återkalla en nyckel

DELETE https://nymbot.ai/api/v1/keys/{id} och undertecknat .

Stänger nyckeln på en gång, för gott. Det stannar i listan med revoked_at installerat, och kan ses med include_revoked=trueÅterkalla en nyckel som redan har återkallats svarar på samma sätt. Endast de senaste 50 återkallade nycklarna behålls; äldre tas bort när en annan nyckel återkallas.

svarar

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

Nya NWC Auto-Top-Up

Anslut en Lightning plånbok med Nostr Wallet Connect och Nymbot toppar upp en balans av sig själv när API-utgifterna kör det lågt. Signerad begäran.

Hur det fungerar: Efter en API-förfrågan debiteras till saldot du valde att titta på, om saldot har sjunkit under din tröskel, gör Nymbot en faktura för ditt toppbelopp, ber din plånbok att betala det och lägger till krediten. Det toppar upp högst en gång var 5: e minut för varje nym och balans, så ett utbrott av förfrågningar kan inte dränera plånboken. Att spendera i apparna utlöser inte det. Tid och storlek på den sista topp-up, och det sista felet, är i inställningarna; om en betalning gick igenom efter ett misstag, kontrollera sin faktura med Top-up status för krediterar det .

Innan du ansluter en plånbok

Nymbot lagrar den krypterad och bara någonsin använder den för att betala sina egna topp-up-fakturor, men gör en anslutning bara för detta, med en utgiftsbudget i din plånbok, så det mesta det någonsin kan betala är ett nummer som valde dig. pay_invoice.

Ansluta en plånbok

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect och undertecknat .

fältetTyp avnödvändigtBeskrivning
nwc_urlSträngarJaAnslutningen sträng, börjar med nostr+walletconnect://Nymbot frågar plånboken för get_info innan du sparar den och lagrar den krypterad.
threshold_satsintegerJaTop upp när saldot faller under dessa många sats.
topup_satsintegerJaHur mycket ska man lägga till varje gång? 1000 till 1 000 000 satser.
tierSträngarNejpro (default) eller standard: balansen att titta på och topp upp.

svarar

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
statusenNär
400Inte en länk till länken (invalid_nwc_url); plånboken svarade inte på sin relä (nwc_unreachable) eller vägrade att kontrollera (nwc_rejected); anslutningen kan inte betala fakturor (nwc_missing_permission) eller ett belopp utanför gränserna.
501Automatisk toppup är inte aktiverad för den här servern (nwc_unavailableDetsamma gäller de andra två slutpunkterna.

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

Läs inställningarna

GET https://nymbot.ai/api/v1/nwc-auto-topup och undertecknat .

Returnerar samma objekt som ansluter, med connected: false och de övriga fälten null När ingen plånbok är ansluten returneras aldrig anslutningssträngen.

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

Avkoppling

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection och undertecknat .

Raderar den sparade anslutningssträngen. Inga fler toppar görs. För att vara säker kan du också återkalla anslutningen i din plånbok.

svarar

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

Betala på begäran utan nyckel

De fasta prisändpunkterna kan betalas för en begäran i taget över Lightning, utan nyckel, utan konto och utan saldo: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations och POST /embeddingsEn begäran som bär en nyckel faktureras till saldot som vanligt; betalningsflödet startar endast när ingen nyckel skickas.

Nymbot talar två versioner av samma idé, från en backend: Lightning Labs' L402 (även accepterat under sitt gamla namn, LSAT) och IETF-förslaget Betalning HTTP-autentisering med hjälp av lightning Metod och charge Använd vad din kund förstår.

Kör din egen server

Betala utan nyckel är bara på när API_L402_SECRET innehåller minst 32 slumpmässiga byte, som hex (64 tecken) eller base64 (44). openssl rand -hex 32Ett kortare eller gissningsbart värde stänger av funktionen och loggar varför. För att rotera det flyttar du det gamla värdet till API_L402_SECRET_PREVIOUS för en dag: credentials, status-URL:er och utmaningar som gjorts under det fortsätter att fungera tills de löper ut.

Utmaningen

Skicka begäran med nej Authorization header. Om det är giltigt, körs inget och du får 402 Payment Required med en faktura för exakt vad den begäran kostar: samma pris som en nyckel skulle betala, omvandlas till 10 sats en standardkredit eller 100 sats en Pro-kredit och avrundas till en hel sat (minst 1 sat och minst 0,05 kreditminimum). WWW-Authenticate Utmaningar för samma faktura:

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

Betalningen request parameter är base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

En utmaning är knuten till slutpunkten, till Content-Type (dennes medietyp och, för multipart, dess gräns) och till SHA-256 av den exakta kroppsbyte du skickade, och varar 15 minuter. identiska Begäran igen: samma Content-Type och samma JSON-byte, eller för de multipartsändpunkterna (/images/edits, /audio/transcriptions, /audio/translationsDe flesta HTTP-bibliotek väljer en ny gräns varje gång de kodar ett formulär, så kodar det en gång och skickar dessa bytes två gånger.

Varje adress kan be om 30 utmaningar per minut (en IPv6-adress räknas som sin helhet /64). Förfrågningar vars adress inte är känd dela en striktare 10 per minut, och det finns en övergripande gräns för utmaningar Nymbot problem över alla adresser; en begäran avvisas innan en utmaning görs (till exempel med en kropp som inte är giltig JSON) räknas inte mot den. 429 med Retry-AfterBetalda slutpunkter som kallas utan nyckel eller behörighet räknas också mot den allmänna gränsen på 120 oautentiserade förfrågningar per minut per adress. Content-Type är inte application/json (eller multipart/form-data för uppladdningar) vägras med 415 och får aldrig en faktura.

Inbäddningar prissätts från en uppskattning av tokenerna i inmatningen, med en marginal på 1,5 gånger, eftersom det verkliga antalet bara är känt efteråt. Återlämna token.

Skicka betalningen

Betala fakturan med någon Lightning plånbok. Plånboken ger dig förbilden, 64 hex tecken. Skicka sedan samma begäran med en av dessa:

Schemaheader
L402Authorization: L402 <macaroon>:<preimage> (LSAT Fungerar också)
BetalningAuthorization: Payment <base64url JSON>Där JSON är {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

En betald begäran svarar precis som en som gjordes med en nyckel, förutom att nymbot Objektet har inga balansfält: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}Och det finns ingen X-Nymbot-Balance-Sats header. En begäran som betalas med betalningssystemet får också en Payment-Receipt header (base64url JSON med utmanings-id, betalningshash som reference, status och timestampBetalda förfrågningar är inte knutna till någon nym, så de visas inte i fråghistoriken.

statusenNär
402 payment_already_usedVarje betalning betalar för en begäran. svaret är en ny utmaning för denna begäran, så en klient som cachar sin senaste credential (som lnget Det är helt enkelt land igen.
402 payment_mismatchCredential utfärdades för en annan endpoint, Content-Type En ny utmaning för denna begäran kommer med det; om betalningen var för liten, vad du betalade kommer tillbaka som en Återlämna token (refund_token och refund_sats och i kroppen).
402 payment_expiredMer än 15 minuter har gått sedan utmaningen. En ny utmaning kommer med den. Om förbilden visar att du har betalat, kommer det du har betalat tillbaka som en Återlämna token (refund_token och refund_sats i kroppen), en gång; kredentialen används sedan.
401 invalid_preimageFörbilden hashar inte till fakturans betalningshash.
401 invalid_payment_credentialTillståndet är felaktigt, ändrades efter att Nymbot utfärdat det, eller namnet på en betalningshash Nymbot aldrig utfärdat en faktura för.
429 rate_limit_exceededMer än 30 autentiseringsuppgifter eller nycklar som misslyckades med att verifiera kom från den här adressen på en minut, eller en återbetalningstoken skickades mer än 60 gånger på en 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"));

Kunderna är byggda på mppx med en blixt metod hantera betalning utmaningen själva; peka dem på slutpunkten och låt dem betala.

Videor

Ett betalt POST /videos Svarar 202 som en nyckel, plus a status_url: GET Det är undertecknat och fungerar i 24 timmar, så länge jobbet hålls.

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

Håll den refund_token från detta svar: det visas bara här. Det är tomt medan videon återger (GET /api/v1/l402/refunds Svarar "status": "pending"); om renderingen misslyckas utan fakturering landar betalningen på den. refund_sats för ett återbetalt jobb men aldrig token, så att dela status-URL-adressen delar inte återbetalningen.

Återbetalningar

Om en betald begäran misslyckas och leverantören fakturerade Nymbot för försöket, behålls betalningen och felet säger så, med charged_sats, precis som för en nycklad begäran. Om den misslyckas utan att faktureras, bär felet en Återlämna token Det är värt vad du betalat:

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

Oanvända delar kommer tillbaka på samma sätt: om du begärde två bilder och en misslyckades med att debitera, framgångsrikt svar nymbot objektet bär en återbetalningstoken för den saknade; en transkription vars längd inte kunde läsas upp i förväg prissätts för den längsta filen kunde vara (aldrig mer än 30 minuter), och skillnaden till den faktiska längden kommer tillbaka som en återbetalningstoken; om det visar sig vara längre än 30 minuter, nekas det med 413 och hela betalningen returneras.Inbäddningar returnerar vad uppskattningen höll tillbaka.En misslyckad video returnerar till den token som dess inlämning returnerades.

En återbetalningstoken är en slumpmässig 256-bitars kod. Nymbot lagrar bara sin hash, och den löper ut efter 30 dagar.

  • Betala med den. Skicka Authorization: Bearer REFUND-… Det är en av de viktigaste faktorerna som påverkar den ekonomiska utvecklingen i Sverige, och det är en del av den ekonomiska utvecklingen i Sverige (refund_token_sats I den nymbot En token värd mindre än förfrågan svarar 402 refund_insufficientEtt misslyckande sätter satsen tillbaka på samma token.
  • Kontrollera det . GET /api/v1/l402/refunds med samma header returnerar {"sats": 237, "status": "open", "expires_at": "..."}.
  • En token kan användas högst 60 gånger per minut.
  • Flytta den till en nym. Sätt in den i Återställ en gåva i Nymbot-appen, eller ringa POST /api/v1/l402/refunds/redeem med a Signerad begäran och {"refund_token": "REFUND-...", "balance": "standard"} (eller "pro"Hela krediter går till saldot (10 satser vardera på standard, 100 på Pro); satser som inte gör en hel kredit stanna på token för API-förfrågningar.

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"

svarar

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