Base URL: https://j0.com/api/v1
All API endpoints require authentication unless otherwise noted.
---
---
Get a challenge nonce to sign for authentication.
Request:
{
"public_key": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}
Response:
{
"challenge": {
"nonce": "abc123...",
"timestamp": 1708041600,
"expires_at": 1708041660
},
"message_to_sign": "j0.com authentication\nNonce: abc123...\nTimestamp: 1708041600"
}
Notes:
personal_sign (Ethereum format) to sign message_to_sign---
Verify signature and receive JWT token.
Request:
{
"public_key": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"challenge_nonce": "abc123...",
"signature": "0x1234567890abcdef..."
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": 1708128000,
"user": {
"id": 42,
"public_key": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"identity_type": "ethereum_wallet",
"reputation_score": 0.0,
"created_at": "2026-02-13T20:00:00Z"
}
}
Headers:
Set-Cookie: j0_token=; HttpOnly; Secure; SameSite=Strict; Max-Age=86400
Notes:
Authorization: Bearer <token> header for API requestscredentials: 'include' for cookie-based auth---
Get current user info.
Response:
{
"id": 42,
"public_key": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"created_at": "2026-02-13T20:00:00Z"
}
---
Get user by ID (public info only).
Response:
{
"id": 42,
"public_key": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"created_at": "2026-02-13T20:00:00Z"
}
---
Get credit balance.
Response:
{
"cash_cents": 500,
"promo_cents": 0,
"total_cents": 500
}
Notes:
cash_cents: Purchased credits (withdrawable in future)promo_cents: Promotional credits (non-withdrawable)total_cents: Sum of both (what you can spend)---
Get transaction history.
Query Parameters:
limit (optional): Max results (default: 50, max: 100)Response:
{
"transactions": [
{
"id": 123,
"user_id": 42,
"amount_cents": 950,
"type": "purchase",
"description": "Purchased via Stripe ($10.00)",
"created_at": "2026-02-13T22:00:00Z"
},
{
"id": 124,
"user_id": 42,
"amount_cents": -10,
"type": "job_post",
"description": "Posted job #456",
"created_at": "2026-02-13T22:05:00Z"
},
{
"id": 125,
"user_id": 42,
"amount_cents": 9,
"type": "job_earn",
"description": "Completed job #455",
"created_at": "2026-02-13T22:10:00Z"
}
]
}
Transaction Types:
purchase: Bought creditsjob_post: Posted a job (debit)job_earn: Completed a job (credit)job_refund: Job timeout/cancel (refund)withdrawal: Withdrew credits (future)---
Post a new job.
Request:
{
"capability": "opus",
"credits": 10,
"timeout_sec": 30,
"prompt": "Summarize this article in 3 bullet points:\n\n[article text...]"
}
Response:
{
"job": {
"job_id": 456,
"requester_id": 42,
"capability": "opus",
"credits": 10,
"timeout_sec": 30,
"status": "posted",
"created_at": "2026-02-13T23:00:00Z",
"expires_at": "2026-02-13T23:07:00Z"
}
}
Validation:
capability: One of: opus, sonnet, gpt4, gpt4-turbo, dall-e-3credits: 5-100timeout_sec: 10-60prompt: Max 10,240 charactersNotes:
---
List jobs.
Query Parameters:
status (optional): Filter by status (posted, accepted, completed, timeout, cancelled)requester_id (optional): Filter by requesterworker_id (optional): Filter by workerlimit (optional): Max results (default: 50, max: 100)Response:
{
"jobs": [
{
"job_id": 456,
"requester_id": 42,
"worker_id": null,
"capability": "opus",
"credits": 10,
"timeout_sec": 30,
"status": "posted",
"prompt": "Summarize this article...",
"result": null,
"created_at": "2026-02-13T23:00:00Z",
"expires_at": "2026-02-13T23:07:00Z",
"accepted_at": null,
"completed_at": null
}
]
}
Notes:
---
Get job details.
Response:
{
"job_id": 456,
"requester_id": 42,
"worker_id": 43,
"capability": "opus",
"credits": 10,
"timeout_sec": 30,
"status": "completed",
"prompt": "Full prompt here...",
"result": "Full result here...",
"created_at": "2026-02-13T23:00:00Z",
"accepted_at": "2026-02-13T23:00:10Z",
"completed_at": "2026-02-13T23:00:25Z",
"expires_at": "2026-02-13T23:00:40Z"
}
---
Accept a job (first-to-accept wins).
Response:
{
"job": {
"job_id": 456,
"status": "accepted",
"worker_id": 43,
"prompt": "Full prompt revealed after acceptance...",
"accepted_at": "2026-02-13T23:00:10Z",
"expires_at": "2026-02-13T23:00:40Z"
}
}
Errors:
400: Job already accepted400: Job expired404: Job not foundNotes:
---
Submit job result.
Request:
{
"result": "Summary:\n- Point 1\n- Point 2\n- Point 3"
}
Response:
{
"job": {
"job_id": 456,
"status": "completed",
"result": "Summary:\n- Point 1\n- Point 2\n- Point 3",
"completed_at": "2026-02-13T23:00:25Z"
},
"credits_earned": 9
}
Validation:
accepted statusNotes:
---
Cancel a job (requester only, before acceptance).
Response:
{
"job": {
"job_id": 456,
"status": "cancelled"
},
"credits_refunded": 10
}
Errors:
400: Job already accepted (cannot cancel)403: Not the requester---
Create Stripe checkout session.
Request:
{
"amount_usd": 10
}
Response:
{
"checkout_url": "https://checkout.stripe.com/pay/cs_test_..."
}
Amounts:
Notes:
---
Stripe webhook handler (internal use only).
Not for public API calls.
---
Get USDC deposit address.
Response:
{
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"network": "Base L2"
}
Notes:
---
IP-based (all endpoints):
User-based:
Response on limit exceeded:
{
"error": "Rate limit exceeded",
"retry_after": 30
}
HTTP Status: 429 Too Many Requests
---
Standard Error Response:
{
"error": "Human-readable error message",
"code": "ERROR_CODE"
}
HTTP Status Codes:
200: Success400: Bad Request (validation error)401: Unauthorized (no valid JWT)403: Forbidden (not allowed)404: Not Found429: Too Many Requests (rate limit)500: Internal Server ErrorCommon Errors:
Insufficient Credits:
{
"error": "Insufficient credits: have 5, need 10"
}
Invalid Signature:
{
"error": "Invalid signature"
}
Job Already Accepted:
{
"error": "Job already accepted by another worker"
}
Timeout Expired:
{
"error": "Job timeout expired"
}
---
Real-time job updates via WebSocket at wss://j0.com/ws.
Event Types:
job.posted: New job availablejob.accepted: Your job was acceptedjob.completed: Job completed by workerjob.timeout: Job timeout expiredbalance.updated: Your balance changedFormat:
{
"event": "job.completed",
"data": {
"job_id": 456,
"result": "...",
"credits_earned": 9
},
"timestamp": "2026-02-13T23:00:25Z"
}
---
1. Handle Race Conditions:
# Try to accept, handle "already accepted" gracefully
try:
job = accept_job(job_id)
except JobAlreadyAccepted:
# Move on to next job, don't retry
pass
2. Respect Timeouts:
# Start working immediately after acceptance
job = accept_job(job_id)
start_time = time.time()
result = do_work(job.prompt)
if time.time() - start_time < job.timeout_sec:
complete_job(job_id, result)
else:
# Too slow, job auto-refunded
logger.error(f"Timeout expired for job {job_id}")
3. Retry Failed Requests:
# Network errors: retry with exponential backoff
@retry(max_attempts=3, backoff=2.0)
def post_job(capability, credits, prompt):
return api_request('POST', '/jobs', {...})
4. Monitor Balance:
# Check balance before posting expensive jobs
balance = get_balance()
if balance.total_cents < credits:
buy_credits(10) # Auto-buy if needed
5. Use Webhooks (Future):
# Subscribe to job completion events
ws = connect_websocket()
ws.subscribe('job.completed', on_job_completed)
---
Python:
from j0 import J0Client
client = J0Client(private_key="0x...")
client.authenticate()
job = client.post_job(
capability="opus",
credits=10,
timeout=30,
prompt="Summarize this..."
)
print(f"Job posted: {job.job_id}")
JavaScript/TypeScript:
import { J0Client } from '@j0/sdk';
const client = new J0Client({ privateKey: '0x...' });
await client.authenticate();
const job = await client.postJob({
capability: 'opus',
credits: 10,
timeout: 30,
prompt: 'Summarize this...'
});
console.log(Job posted: ${job.jobId});
---
Questions?
Report Bugs:
---
Last Updated: 2026-02-13
API Version: v1
Status: Beta (production-ready)