StellarKraal exposes a versioned REST API for livestock-backed lending on Stellar/Soroban. This guide takes you from zero to your first successful API call in under ten minutes.
| Environment | Base URL |
|---|---|
| Local development | http://localhost:3001 |
| Staging | https://api-staging.stellarkraal.example.com |
All examples below use http://localhost:3001. Replace with the staging URL when testing against
the hosted environment.
The versioned path prefix is /api/v1. New integrations should always use the versioned prefix.
The interactive OpenAPI / Swagger UI is served at:
- Local:
http://localhost:3001/api/docs - Full spec source:
backend/openapi.json
The API uses a wallet-based challenge–sign–login flow. You need a Stellar key pair and a
signature tool (stellar-cli or Freighter) to complete it.
curl http://localhost:3001/auth/challengeResponse:
{ "challenge": "stellarkraal:1721900000000:abc123def456" }Sign the raw challenge bytes with your Stellar secret key. Using stellar-cli:
# Store the challenge in a variable
CHALLENGE=$(curl -s http://localhost:3001/auth/challenge | jq -r '.challenge')
# Sign it (replace S... with your secret key)
SIGNATURE=$(echo -n "$CHALLENGE" | stellar keys sign --key <YOUR_SECRET_KEY> --stdin)curl -s -X POST http://localhost:3001/auth/login \
-H "Content-Type: application/json" \
-d "{
\"publicKey\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN\",
\"signature\": \"$SIGNATURE\",
\"challenge\": \"$CHALLENGE\"
}"Response:
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiJ9...",
"expiresIn": 900
}accessTokenis valid for 15 minutes. Pass it asAuthorization: Bearer <token>on every protected request.refreshTokenis valid for 7 days. UsePOST /auth/refreshto obtain a fresh pair before the access token expires.
Store the token:
TOKEN="eyJhbGciOiJIUzI1NiJ9..."Retrieve collateral records owned by your account. This endpoint is paginated.
curl -s "http://localhost:3001/api/v1/collateral?page=1&pageSize=10" \
-H "Authorization: Bearer $TOKEN"Response:
{
"data": [
{
"id": "1",
"owner": "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN",
"animal_type": "cattle",
"count": 5,
"appraised_value": 5000000,
"deleted_at": null
}
],
"total": 1,
"page": 1,
"pageSize": 10
}Register livestock as collateral. The endpoint returns an unsigned XDR transaction that your wallet must sign and submit to the Stellar network.
curl -s -X POST http://localhost:3001/api/v1/collateral/register \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owner": "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN",
"animal_type": "cattle",
"count": 5,
"appraised_value": 5000000
}'Response:
{
"xdr": "AAAAAgAAAAA...",
"collateral_id": 42
}Sign and submit the XDR using stellar-cli:
stellar transaction sign --xdr "AAAAAgAAAAA..." --source <YOUR_SECRET_KEY> --network testnet
stellar transaction submit --xdr "<SIGNED_XDR>" --network testnetIn a browser context, use Freighter — see the Freighter Integration Guide for details.
Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
owner |
string | ✓ | Stellar public key (G…) of the collateral owner |
animal_type |
string | ✓ | Type of livestock: cattle, goat, sheep, etc. |
count |
integer | ✓ | Number of animals in this collateral group |
appraised_value |
integer | ✓ | Total appraised value in token base units |
Request a loan against a registered collateral ID.
curl -s -X POST http://localhost:3001/api/v1/loan/request \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"borrower": "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN",
"collateral_id": 42,
"amount": 3000000
}'Response:
{
"xdr": "AAAAAgAAAAA...",
"loan_id": 7
}Sign and submit the returned XDR as shown in Step 3. Once confirmed on-chain, the loan state
transitions to Active.
Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
borrower |
string | ✓ | Stellar public key of the borrower |
collateral_id |
integer | ✓ | ID returned from the collateral registration step |
amount |
integer | ✓ | Gross loan amount in token base units |
# Fetch a specific loan
curl -s "http://localhost:3001/api/v1/loan/7" \
-H "Authorization: Bearer $TOKEN"
# Check the health factor
curl -s "http://localhost:3001/api/v1/health/7" \
-H "Authorization: Bearer $TOKEN"A health factor below 1.0 puts the loan at_risk. See the
Loan State Machine and
Liquidation Mechanism for details.
curl -s -X POST http://localhost:3001/auth/refresh \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "<YOUR_REFRESH_TOKEN>" }'All error responses follow a consistent JSON envelope:
{ "error": "UNAUTHORIZED", "message": "Token expired or invalid." }HTTP status codes follow standard conventions: 400 validation error, 401 unauthenticated,
403 forbidden, 404 not found, 429 rate limited, 500 server error.
Full error code reference: API Error Codes.
All clients are subject to per-IP rate limits. Retry using the Retry-After header value when
you receive a 429 response. Full details: Rate Limits Guide.
| Resource | Description |
|---|---|
| OpenAPI Spec | Machine-readable full API specification |
| Swagger UI | Interactive API explorer (local only) |
| Auth Flow | Detailed challenge–sign–login flow and token rotation |
| API Integration Tutorial | End-to-end external app integration with webhooks |
| Rate Limits | Per-tier limits, headers, and retry behaviour |
| Freighter Integration | Signing XDR with the Freighter browser extension |
| Local Setup | Full local development environment setup |
| Troubleshooting | Common errors and resolutions |