Top Up Credits
curl --request POST \
--url https://win.oneshotagent.com/v1/credits/top-up \
--header 'Content-Type: application/json' \
--data '
{
"amount": 123,
"memo": "<string>"
}
'import requests
url = "https://win.oneshotagent.com/v1/credits/top-up"
payload = {
"amount": 123,
"memo": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({amount: 123, memo: '<string>'})
};
fetch('https://win.oneshotagent.com/v1/credits/top-up', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://win.oneshotagent.com/v1/credits/top-up",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 123,
'memo' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://win.oneshotagent.com/v1/credits/top-up"
payload := strings.NewReader("{\n \"amount\": 123,\n \"memo\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://win.oneshotagent.com/v1/credits/top-up")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 123,\n \"memo\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://win.oneshotagent.com/v1/credits/top-up")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 123,\n \"memo\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data.topped_up": "<string>",
"data.credits_balance": "<string>",
"data.transaction_id": "<string>",
"data.settlement_tx": "<string>"
}Credits
Top Up Credits
Add prepaid credits by paying the same amount in USDC via x402
POST
/
v1
/
credits
/
top-up
Top Up Credits
curl --request POST \
--url https://win.oneshotagent.com/v1/credits/top-up \
--header 'Content-Type: application/json' \
--data '
{
"amount": 123,
"memo": "<string>"
}
'import requests
url = "https://win.oneshotagent.com/v1/credits/top-up"
payload = {
"amount": 123,
"memo": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({amount: 123, memo: '<string>'})
};
fetch('https://win.oneshotagent.com/v1/credits/top-up', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://win.oneshotagent.com/v1/credits/top-up",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 123,
'memo' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://win.oneshotagent.com/v1/credits/top-up"
payload := strings.NewReader("{\n \"amount\": 123,\n \"memo\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://win.oneshotagent.com/v1/credits/top-up")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 123,\n \"memo\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://win.oneshotagent.com/v1/credits/top-up")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 123,\n \"memo\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data.topped_up": "<string>",
"data.credits_balance": "<string>",
"data.transaction_id": "<string>",
"data.settlement_tx": "<string>"
}
Overview
Adds prepaid credits from the agent’s own wallet. The requestedamount is the price: you pay
amount USDC on-chain through x402, and amount is added to the credit balance.
Access-token sessions (Grok Bot, cloud runners, any client
without a wallet key) spend those credits. The top-up takes two calls:
- Call with
{ amount }and no payment. You get a 402 with apayment_requestand aPAYMENT-REQUIREDheader priced at exactly that amount. - Sign the USDC authorization and call again with the
payment-signatureheader. The payment settles on-chain, the credit is recorded, and you get a 200.
const r = await agent.topUpCredits(25);
console.log(r.credits_balance); // "25.000000"
r = client.top_up_credits(25)
Authentication
Wallet session only (X-Agent-ID + payment signature). An access-token
session gets 503 route_unavailable_for_access_tokens, since credits can’t buy
credits. Operators can grant credits directly with
POST /v1/tools/internal/credits/grant (or, for a wallet the API has never seen,
POST /v1/tools/internal/agents/register with the same grant fields).
Request
number
required
USDC to add, from 0.01 to 1000. Also the amount paid.
string
Note stored on the ledger row (max 1000 chars).
Response
string
USDC credited, 6 decimals.
string
Credit balance after the top-up.
string
Ledger row id.
string
On-chain settlement transaction hash. Also the idempotency key: resubmitting a settled payment returns the same row with
already_credited: true.Errors
| Status | error | Meaning |
|---|---|---|
| 400 | invalid_amount | Not a number, or outside 0.01 to 1000 |
| 402 | payment_required | First call: pay the amount in the PAYMENT-REQUIRED header |
| 402 | payment_verification_failed | Payment did not verify or settle; nothing credited |
| 500 | top_up_settled_not_credited | Payment settled but the ledger write failed. Retry with the same payment (idempotent) or give the operator the settlement_tx |
| 503 | route_unavailable_for_access_tokens | Access-token session; use the wallet session |