Spring til indholdet
Tilbage til Nymbot

Vidensgrundlag Udviklere

Balance, top-ups og nøgler

Tjek, hvad du har, top op over Lightning, top op automatisk fra din egen tegnebog, se, hvad hver anmodning koster, og administrer nøgler fra kode.

Tjek balancen

Både dine balancer, og hvor meget af denne nøgles cap bruges.

GET https://nymbot.ai/api/v1/credits/balance Det kræver en API-nøgle. POST Det gælder også for de kunder, der forventer det.

balance er de to saldi sammen i dollars ved den nuværende Bitcoin pris, for værktøjer, der forventer et enkelt tal (null Resten er i kreditter og satser, hvilket er, hvordan balancerne rent faktisk holdes. key En nøgle, der har nået sin grænse, kan stadig tjekke balancen.

Svaret

{
  "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"
  }
}
Status erNår
401Nøglen er manglende, ukendt, tilbagekaldt eller udløbet.

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

Betalingsmetoder

Hvordan du kan toppe op, og grænserne. Lightning er den eneste metode.

GET https://nymbot.ai/api/v1/topup/payment-methods Ingen nøgle kræves.

En top-up er 10 til 1.000.000 sats; en Pro top-up skal købe mindst en Pro kredit, så det starter på 100 sats. bulk_bonus Lister den ekstra kredit på større top-ups, det samme som i appen: 10%, 15% eller 20% mere på standard top-ups fra 500, 1.000 eller 5.000 satser, og på Pro top-ups fra 5.000, 10.000 eller 50.000 satser.

Svaret

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

Løber op over lyn

Opret en Lightning faktura, der tilføjer kredit til nym nøglen tilhører. betale det fra enhver Lightning tegnebog, så Tjek det For at få kreditten tilføjet.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Det kræver en API-nøgle.

FeltetType afKrævesBeskrivelse af
amountNummeretJaHvor meget, i currencyEt helt tal for sats.
currencyStrengeNej ikkeSATS (Det er et underskud) USD eller BTCDollars konverteres til den nuværende Bitcoin pris.
tierStrengeNej ikkepro (den forældede) eller standardHvilken balance kreditten går til.

En standard kredit er 10 satser og en Pro kredit 100 satser, plus enhver bulk bonus; credits Det vil sige, hvad denne faktura vil tilføje.

Svaret

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
Status erNår
400En anden metode på vejen (unsupported_method) en ukendt valuta (unsupported_currencyeller et beløb, der mangler, eller et beløb under minimumsbeløbet (amount_too_small) over 1.000.000 satser (amount_too_large) eller afvises af Lightning wallet (amount_out_of_range).
429Mere end 60 fakturaer for denne nym, eller 120 fra denne adresse, på en time (rate_limit_exceeded, med Retry-After).
502Der kan ikke indgives en faktura i øjeblikket (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);

Tjek en top-up

Spørger, om regningen er blevet betalt, og når den er, tilføjer den kredit. Tjek er, hvad kreditter det, så efter at have betalt, tjek, indtil status er creditedTjek igen efterfølgende er sikkert: kredit lander én gang, uanset hvor mange gange du spørger.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - har brug for en nøgle fra nym, der lavede fakturaen.

status er pending (Det er ikke betalt endnu) paid (betalt, men endnu ikke krediteret; tjek igen), credited (på din balance) eller expired (ikke betalt i tide).Balancefelterne er for det niveau, som fakturaen topper op.

Svaret

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
Status erNår
400Id'et er ikke det 64-tegns id fra oprettelsesopkaldet.
404Ingen faktura af denne id for 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);

Ønsker historie

En række pr. anmodning: hvad det var, hvilken model, hvor mange tokens og hvad det kostede. Ingen opfordringer eller svar opbevares, så ingen returneres. Rækker opbevares i 90 dage, nyeste først. En nøgle, der har nået sin grænse, kan stadig læse sin historie.

GET https://nymbot.ai/api/v1/queries/history - har brug for en API-nøgle, som kan se sine egne anmodninger, eller en Underskrevet anmodning af din nym, som ser hver nøgles.

FeltetType afKrævesBeskrivelse af
pageDet er et spørgsmål (query)Nej ikkeStandard 1, højst 1000; en højere side er 400 invalid_value.
page_countDet er et spørgsmål (query)Nej ikkeRækker pr. side. Standard 20, højst 100.
start_date
end_date
String (Kørsel)Nej ikkeISO 8601 datoer og tider.
modelString (Kørsel)Nej ikkeKun denne model.
typeString (Kørsel)Nej ikkechat, responses, messages, image, video, speech, transcription eller embedding.
all_keysaf Boolean (Query)Nej ikkeMed en nøgle: true Indeholder alle nøgler af samme nym. false.
key_idString (Kørsel)Nej ikkeMed en underskrevet ansøgning, eller med all_keys=trueKun denne nøgle.

Svaret

{
  "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 af kontoanmodninger

Oprettelse, ændring og tilbagekaldelse af nøgler, kontooversigt og automatiske top-ups tager ikke en API-nøgle.De tager en signatur fra din nym, så en lækket nøgle kan bruge op til sin cap, men kan aldrig lave en anden nøgle eller hæve sin egen cap.

App'en gør dette for dig: alt i sin ild Du har kun brug for denne sektion til at administrere nøgler fra din egen kode.

Signaturen er en Nostr-begivenhed af typen 27235 (NIP-98), sendt base64-kodet i Authorization Header med ordet Nostr På forsiden:

Begivenheden

{
  "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 er den fulde URL-adresse for anmodningen, herunder forespørgselsstrengen, nøjagtigt som sendt.
  • method Det er HTTP metoden.
  • payload er SHA-256 af den rå anmodning krop, i hex. Det kræves på POST og PATCH, og den krop, du sender, skal være byte for byte den du hashede.
  • created_at Det skal være inden for 60 sekunder af serverens klokke.
  • Hver begivenhed fungerer én gang, a GET inkluderet, så en fanget overskrift ikke kan afspilles igen. Underskrive en ny for hver anmodning. nonce tag med en tilfældig værdi, så to anmodninger, der er underskrevet i samme sekund, stadig er forskellige.
  • Kroppen af en underskrevet anmodning kan højst være 64 KB, og en krop skal Content-Type: application/json.

En manglende begivenhed vender tilbage 401 missing_nostr_authen, der er dårligt dannet, dårligt underskrevet, for gammel eller til en anden URL, metode eller krop returnerer invalid_nostr_auth, med årsagen i meddelelsen; en genbrugt en returnerer nostr_auth_replayedEn API-nøgle sendt til disse endpoints afvises. underskriften kontrolleres, før kroppen læses, og hver adresse kan fejle det 30 gange i minuttet (en IPv6-adresse tæller som sin helhed /64); derefter får den 429 med Retry-After.

Browsere kan kun kalde disse slutpunkter fra Nymbots egne websteder (https://nymbot.ai, https://nymchat.app En side på et andet websted får ingen CORS-overskrifter tilbage, så den ikke kan læse, hvad de returnerer. OriginDe er ikke berørt.

Din hemmelige nøgle

Signering kræver din nym's hemmelige nøgle (den nsec), som styrer alt: din identitet, din historie og din balance. Kun sætte det i et script på en maskine, du stoler på, læse det fra miljøet i stedet for at skrive det i filen, og foretrækker appen, når du kan.

Disse hjælpere bygger overskriften. De senere eksempler på denne side bruger dem. De læser den hemmelige nøgle i hex fra NOSTR_SECRET_HEX; cURL en bruger den Nak kommandolinjeværktøj, der tager en nsec eller hex-nøgle, og sha256sum På 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 sammenfatning

Hvad appens API-ark viser øverst: din offentlige nøgle, begge balancer, hvor mange nøgler der er aktive (ikke tilbagekaldt eller udløbet), og Automatisk top-up indstillinger, eller null når serveren ikke tilbyder dem.

GET https://nymbot.ai/api/v1/account Har brug for a Underskrevet anmodning.

Svaret

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

Styring af nøgler

Endpunkterne bag app'ens nøgleliste. Alle har brug for en Underskrevet anmodningHver nøgle returneres i denne form, med tider i ISO 8601 og beløb i sats:

Nøgleobjekt

{
  "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 er nok at genkende en nøgle, men ikke at bruge den.Nøglen returneres kun én gang, når den er lavet.

Liste over nøgler

GET https://nymbot.ai/api/v1/keys og underskrevet.

FeltetType afKrævesBeskrivelse af
include_revokedaf Boolean (Query)Nej ikkeInkluderer tilbagekaldte nøgler. false.

Svaret

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

Gør en nøgle

POST https://nymbot.ai/api/v1/keys Underskrevet - vender tilbage 201.

FeltetType afKrævesBeskrivelse af
nameStrengeJa1 til 40 tegn, der adskiller sig fra dine andre aktive nøgler (ignorerende tilfælde).
limit_satsIntegerNej ikkeIndtjeningskapaciteten i sats, mindst 1.
reset_periodStrengeNej ikkedaily, weekly eller monthlyBehovet limit_satsLæg det ud for en hætte, der aldrig genopretter.
expire_atString eller integerNej ikkeNår nøglen stopper med at arbejde: en ISO 8601-tid, eller millisekunder siden 1970.

Svar på spørgsmål (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"
  }
}
Status erNår
400Et manglende eller for langt navn; et navn, der allerede er i brug (duplicate_name); en cap, der ikke er et helt tal på mindst 1; en reset periode uden cap; en udløb i fortiden; et ukendt felt (unknown_parameter); eller 25 aktive nøgler allerede (too_many_keys).
429Mere end 60 nøgler lavet af denne nym, eller 120 fra denne adresse, på en time (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 nøgle

GET https://nymbot.ai/api/v1/keys/{id} og underskrevet.

Tilbage {"data": {…}} med nøgleobjektet, eller 404 key_not_found Hvis ingen af dine nøgler har dette 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);

Ændring af nøgle

PATCH https://nymbot.ai/api/v1/keys/{id} og underskrevet.

Send en af name, limit_sats, reset_period og expire_atmed de samme regler som når man laver en nøgle. null fjerne et felt: ingen cap, ingen reset, ingen udløb. Ændring af resetperioden starter en ny periode ved nul. En tilbagekaldt nøgle kan ikke ændres (400 key_revoked) vender tilbage {"data": {…}} med det opdaterede nøgleobjekt.

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

Tilbagekaldelse af nøgle

DELETE https://nymbot.ai/api/v1/keys/{id} og underskrevet.

Stopper nøglen med det samme, for godt. Det forbliver på listen med revoked_at set, og kan ses med include_revoked=trueTilbagekaldelse af en nøgle, der allerede er tilbagekaldt, svarer på samme måde.Kun de nyeste 50 tilbagekaldte nøgler bevares; ældre nøgler slettes, når en anden nøgle tilbagekaldes.

Svaret

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

Næste Næste indlæg: NWC Auto-top-up

Tilslut en Lightning tegnebog med Nostr Wallet Connect og Nymbot topper en balance af sig selv, når API-udgifter kører det lavt. Appens API-ark har de samme indstillinger; disse er slutpunkterne bag det. Underskrevet anmodning.

Hvordan det virker: Efter en API-anmodning er opkrævet til den balance, du valgte at se, hvis den balance er faldet under din tærskel, laver Nymbot en faktura for dit top-up beløb, beder din tegnebog om at betale det, og tilføjer kredit. Det topper op højst en gang hvert 5. minut for hver nym og balance, så et udbrud af anmodninger ikke kan dræne tegnebogen. Udgifter i apps udløser det ikke. Tid og størrelse på den sidste top-up, og den sidste fejl, er i indstillingerne; hvis en betaling gik igennem efter en fejl, tjekke sin faktura med Top-up status krediterer det.

Før du tilslutter en tegnebog

Nymbot gemmer det krypteret og kun nogensinde bruger det til at betale sine egne top-up fakturaer, men gøre en forbindelse bare for dette, med et udgiftsbudget i din tegnebog, så det mest det nogensinde kunne betale er et nummer valgte dig. pay_invoice.

Tilslutning af en tegnebog

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect og underskrevet.

FeltetType afKrævesBeskrivelse af
nwc_urlStrengeJaForbindelsesstrengen, der starter med nostr+walletconnect://Nymbot beder om en tegnebog get_info før du gemmer det, og gemmer det krypteret.
threshold_satsIntegerJaTop op, når saldoen falder under disse mange sats.
topup_satsIntegerJaHvor meget skal du tilføje hver gang. 1.000 til 1.000.000 satser.
tierStrengeNej ikkepro (den forældede) eller standardBalancen til at se og top op.

Svaret

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
Status erNår
400Der er ikke tale om en linje (invalid_nwc_url); tegnebogen svarede ikke på sin relæ (nwc_unreachable) eller afviste kontrollen (nwc_rejected); forbindelsen kan ikke betale fakturaer (nwc_missing_permissioneller et beløb uden for grænserne.
501De automatiske top-ups er ikke tændt for denne server (nwc_unavailableDet samme gælder for de to andre slutpunkter.

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 indstillingerne

GET https://nymbot.ai/api/v1/nwc-auto-topup og underskrevet.

Returnerer det samme objekt som forbinder, med connected: false og de øvrige felter null Når der ikke er tilsluttet en tegnebog, returneres forbindelsesstrengen aldrig.

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

Afbrydelse af

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection og underskrevet.

Sletter den gemte forbindelsesstreng. Der oprettes ikke flere top-ups. For at være sikker kan du også tilbagekalde forbindelsen i din tegnebog.

Svaret

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

Betal efter anmodning uden nøgle

De faste pris-endpoint kan betales for én anmodning ad gangen over Lightning, uden nøgle, uden konto og uden balance: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations og POST /embeddingsChat, svar og beskeder har altid brug for en nøgle.En anmodning, der bærer en nøgle, faktureres til saldoen som sædvanlig; betalingsstrømmen starter kun, når ingen nøgle sendes.

Nymbot taler to versioner af den samme idé, fra en backend: Lightning Labs' K402 (Også accepteret under sit gamle navn, LSAT) og IETF-udkastet Betaling af HTTP-godkendelsesordningen med den lightning Metode og charge Brug, hvad din kunde forstår.

Kør din egen server

Betaling uden nøgle er kun aktiveret, når API_L402_SECRET indeholder mindst 32 tilfældige byte, som hex (64 tegn) eller base64 (44). openssl rand -hex 32En kortere eller gættelig værdi slukker funktionen og logger hvorfor. API_L402_SECRET_PREVIOUS for en dag: legitimationsoplysninger, status URL'er og udfordringer lavet under det fortsætter med at arbejde, indtil de udløber.

Udfordringen

Send forespørgslen med nej Authorization header. Hvis det er gyldigt, kører intet, og du får 402 Payment Required med en faktura for præcis, hvad den anmodning koster: den samme pris en nøgle ville betale, konverteret til 10 sats en standard kredit eller 100 sats en Pro kredit og afrundet til en hel sat (mindst 1 sat, og mindst 0.05 kredit minimum). WWW-Authenticate Udfordringer for samme ansøgning:

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

Betalingen request En af de vigtigste parametre er base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

En udfordring er knyttet til slutpunktet, til Content-Type (deres medie type og, for multipart, dens grænse) og til SHA-256 af den nøjagtige krop bytes du sendte, og varer 15 minutter. identisk Forespørgsel: Det samme Content-Type og de samme JSON bytes, eller for de multiparts endpoint (/images/edits, /audio/transcriptions, /audio/translationsDe fleste HTTP-biblioteker vælger en ny grænse hver gang de koder en formular, så kod den en gang og send disse bytes to gange.

Hver adresse kan anmode om 30 udfordringer pr. minut (en IPv6-adresse tæller som sin helhed /64). Anmodninger, hvis adresse ikke er kendt, deler en strengere 10 pr. minut, og der er en samlet grænse for udfordringerne Nymbot problemer på tværs af alle adresser; en anmodning afvist før en udfordring er lavet (for eksempel med en krop, der ikke er gyldig JSON) tæller ikke mod det. 429 med Retry-AfterBetalte slutpunkter, der kaldes uden en nøgle eller credential, tæller også mod den generelle grænse på 120 uautentiserede anmodninger pr. minut pr. adresse. Content-Type Er ikke application/json (eller multipart/form-data for uploads) er nægtet med 415 Og får aldrig en faktura.

Embeddings prissættes ud fra en estimering af tokens i input, med en margin på 1,5 gange, da det faktiske antal kun kendes bagefter. Tilbagebetaling af token.

Send betalingen

Betal regningen med enhver Lightning tegnebog. tegnebogen giver dig forbilledet, 64 hex tegn. Så send den samme anmodning med en af disse:

SkemaetHeader
K402Authorization: L402 <macaroon>:<preimage> (LSAT Det virker også)
Betaling afAuthorization: Payment <base64url JSON>Hvor JSON er placeret {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

En betalt anmodning svarer nøjagtigt som en, der er lavet med en nøgle, bortset fra at nymbot Objektet har ingen balancefelter: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}Og der er ikke X-Nymbot-Balance-Sats En anmodning betalt med betalingsordningen modtager også en Payment-Receipt header (base64url JSON med udfordring id, betaling hash som reference, status og timestampBetalte forespørgsler er ikke bundet til nogen nym, så de vises ikke i forespørgselshistorikken.

Status erNår
402 payment_already_usedHver betaling betaler for en anmodning. svaret er en ny udfordring for denne anmodning, så en klient, der cacher sin sidste credential (som lnget Det er simpelthen land igen.
402 payment_mismatchOpgaven blev udstedt for et andet endpoint. Content-Type eller krop, eller betale mindre end denne anmodning nu koster. En ny udfordring for denne anmodning kommer med det; hvis betalingen var for lille, hvad du betalte kommer tilbage som en Tilbagebetaling af token (refund_token og refund_sats i den indre krop).
402 payment_expiredMere end 15 minutter er gået siden udfordringen. En ny udfordring kommer med den. Hvis forbilledet viser, at du har betalt, kommer det, du har betalt, tilbage som en Tilbagebetaling af token (refund_token og refund_sats i kroppen), en gang; credentialen bliver derefter brugt op.
401 invalid_preimageForbilledet hashes ikke til fakturaens betalingshash.
401 invalid_payment_credentialOplysningerne er misdannede, blev ændret, efter at Nymbot udstedte dem, eller navngav en betalingshash, som Nymbot aldrig udstedte en faktura for.
429 rate_limit_exceededMere end 30 legitimationsoplysninger eller nøgler, der ikke kunne verificeres, kom fra denne adresse på et minut, eller en refunderingstoken blev sendt mere end 60 gange i et 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"));

Kunderne er bygget på mppx med en Lightning-metode håndtere betalingsudfordringen selv; peg dem på slutpunktet og lad dem betale.

Videoer

En betalt POST /videos Svarer 202 som en nøgle, plus a status_url: GET Det er underskrevet og fungerer i 24 timer, så længe jobbet holdes.

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

Hold den refund_token Det er kun her, der vises svaret. Det er tomt, mens videoen gengiver (GET /api/v1/l402/refunds Svarer "status": "pending"); hvis renderingen ikke er faktureret, lander betalingen på den. refund_sats for et refunderet job, men aldrig token, så deling af status-URL'en deler ikke refusionen.

Tilbagebetaling

Hvis en betalt anmodning mislykkes, og udbyderen fakturerede Nymbot for forsøget, opbevares betalingen, og fejlen siger det, med charged_sats, præcis som for en nøgleforespørgsel. Hvis den mislykkes uden at blive faktureret, medfører fejlen en Tilbagebetaling af token Værd hvad du har betalt:

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

Ubrugte dele returneres på samme måde: Hvis du har bedt om to billeder, og et af dem mislykkedes, lykkes svaret. nymbot objekt bærer en refundering token for den manglende; en transkription, hvis længde ikke kunne læses op forud, prissættes for den længste fil kunne være (aldrig mere end 30 minutter), og forskellen til den faktiske længde kommer tilbage som en refundering token; hvis det viser sig at være længere end 30 minutter, er det afvist med 413 Embeddings returnerer, hvad estimatet holdt tilbage. En mislykket video refunderer den token, dens indsendelse returnerede.

Et refunderingstoken er en tilfældig 256-bit kode. Nymbot gemmer kun sin hash, og den udløber efter 30 dage.

  • Betal med det. Send mig Authorization: Bearer REFUND-… På den ene side er der en afdeling, hvor der er tale om en afdeling, og på den anden side er der en afdeling, hvor der er tale om en afdeling, hvor der er tale om en afdeling (refund_token_sats I den nymbot En token, der er mindre værd end svarene på anmodningen 402 refund_insufficientEn mislykket fejl sætter satsen tilbage på samme token.
  • Tjek det GET /api/v1/l402/refunds med samme header returnerer {"sats": 237, "status": "open", "expires_at": "..."}.
  • En token kan bruges op til 60 gange i minuttet.
  • Flyt den til en nym. Sæt den i Giv en gave i Nymbot-appen, eller ringe POST /api/v1/l402/refunds/redeem med a Underskrevet anmodning og {"refund_token": "REFUND-...", "balance": "standard"} (eller "pro"Hele kreditter går til saldoen (10 satser hver på standard, 100 på Pro); satser, der ikke gør en hel kredit ophold på token for API-forespørgsler.

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"

Svaret

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