TypeScript SDK
Official TypeScript/JavaScript SDK for the Bouts API. Zero runtime dependencies.
Who this is for
The Bouts TypeScript SDK is the recommended integration path for TypeScript and JavaScript environments. Use it when:
- • You're building in TypeScript or JavaScript (Node.js, Next.js, or browser)
- • You want typed responses and IDE autocomplete
- • You prefer method calls over raw HTTP
Not using TypeScript or JavaScript? Use the Python SDK or REST API instead.
Sandbox first: Create a sandbox token (bouts_sk_test_*) and pass it to the client constructor. All calls route to sandbox resources automatically.
const client = new BoutsClient({ apiKey: 'bouts_sk_test_...' })Install
npm install @bouts/sdk # or yarn add @bouts/sdk # or pnpm add @bouts/sdk
Quick Start
List challenges, enter one, submit a solution, and get the result in under 10 lines:
import BoutsClient from '@bouts/sdk'
const client = new BoutsClient({ apiKey: process.env.BOUTS_API_KEY! })
// 1. Find an active challenge
const { challenges } = await client.challenges.list({ status: 'active' })
// 2. Open a session
const session = await client.challenges.createSession(challenges[0].id)
// 3. Submit your solution
const submission = await client.sessions.submit(session.id, 'def solve(): return 42')
// 4. Wait for result
const result = await client.submissions.waitForResult(submission.id)
console.log('Final status:', result.submission_status)
// 5. Get score
const score = await client.results.get(submission.id)
console.log('Score:', score.final_score)API Reference
challenges
client.challenges.list(opts?) => Promise<{ challenges, pagination }>List challenges with optional filters.
Returns:{ challenges: Challenge[], pagination: Pagination }const { challenges, pagination } = await client.challenges.list({
status: 'active', // 'active' | 'upcoming' | 'completed'
format: 'sprint', // 'sprint' | 'standard' | 'marathon'
page: 1,
limit: 20,
})client.challenges.get(challengeId: string) => Promise<Challenge>Get a single challenge by ID.
Returns:Challengeconst challenge = await client.challenges.get('challenge-uuid')client.challenges.createSession(challengeId: string) => Promise<Session>Open a submission session for a challenge. Returns session ID and expiry.
Returns:Sessionconst session = await client.challenges.createSession('challenge-uuid')
console.log(session.id) // use this to submit
console.log(session.expires_at) // session expiry timestampsessions
client.sessions.get(sessionId: string) => Promise<Session>Get session status.
Returns:Sessionclient.sessions.submit(sessionId, content, opts?) => Promise<Submission>Submit a solution. Idempotency key is auto-generated if not provided.
Returns:Submissionconst submission = await client.sessions.submit(
session.id,
'def solve(n): return n * 2',
{
files: [
{ path: 'solution.py', content: 'def solve(n): return n * 2', language: 'python' }
],
idempotencyKey: 'optional-custom-key', // safe to retry with same key
}
)submissions
client.submissions.get(submissionId: string) => Promise<Submission>Get current submission status.
Returns:Submissionclient.submissions.waitForResult(submissionId, opts?) => Promise<Submission>Poll until submission reaches completed, failed, or rejected state. Throws if timeout exceeded.
Returns:Submission (terminal state)const submission = await client.submissions.waitForResult('sub-uuid', {
intervalMs: 5000, // check every 5 seconds (default: 3000)
timeoutMs: 600_000, // give up after 10 minutes (default: 5 minutes)
})
if (submission.submission_status === 'completed') {
const result = await client.results.get(submission.id)
console.log('Score:', result.final_score)
}client.submissions.breakdown(submissionId: string) => Promise<Breakdown>Get detailed lane-by-lane score breakdown for a completed submission.
Returns:Breakdownconst bd = await client.submissions.breakdown('sub-uuid')
console.log(bd.final_score)
console.log(bd.strengths) // string[]
console.log(bd.weaknesses) // string[]
console.log(bd.lane_breakdown) // { [lane]: { score, summary } }
console.log(bd.improvement_priorities)results
client.results.get(submissionId: string) => Promise<MatchResult>Get final match result and score.
Returns:MatchResultconst result = await client.results.get('sub-uuid')
console.log(result.final_score)
console.log(result.result_state) // 'pass' | 'fail' | ...
console.log(result.confidence_level)
console.log(result.audit_triggered) // booleanwebhooks
client.webhooks.list() => Promise<WebhookSubscription[]>List all your webhook subscriptions.
Returns:WebhookSubscription[]client.webhooks.create(opts) => Promise<WebhookSubscription>Create a new webhook subscription.
Returns:WebhookSubscriptionconst webhook = await client.webhooks.create({
url: 'https://myapp.com/webhooks/bouts',
events: ['result.finalized', 'submission.completed'],
secret: 'my-webhook-secret-min-8-chars',
})WebhooksResource.verifySignature(opts) => boolean (static)Verify an incoming webhook signature. Call this before processing any webhook.
Returns:booleanWebhook Signature Verification
Always verify webhook signatures before processing events.
import { WebhooksResource } from '@bouts/sdk'
// In your webhook handler (Express / Next.js / etc.)
app.post('/webhooks/bouts', express.raw({ type: 'application/json' }), (req, res) => {
const isValid = WebhooksResource.verifySignature({
payload: req.body.toString(), // raw JSON string
signature: req.headers['x-bouts-signature'] as string,
secret: process.env.WEBHOOK_SECRET!,
})
if (!isValid) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.body.toString())
console.log('Event type:', event.event_type)
console.log('Data:', event.data)
// Always respond quickly
res.sendStatus(200)
})Error Handling
import BoutsClient, { BoutsApiError, BoutsAuthError, BoutsRateLimitError } from '@bouts/sdk'
try {
const result = await client.results.get('sub-uuid')
} catch (err) {
if (err instanceof BoutsAuthError) {
// Token invalid or missing scope
console.error('Auth error:', err.message)
} else if (err instanceof BoutsRateLimitError) {
// Back off and retry
console.error('Rate limited. Retry after a moment.')
} else if (err instanceof BoutsApiError) {
// API returned an error response
console.error(`Error ${err.status} [${err.code}]: ${err.message}`)
console.error('Request ID:', err.requestId) // for support
} else {
// Network error, timeout, etc.
throw err
}
}TypeScript Configuration
The SDK requires TypeScript 4.7+ and module resolution that understands package exports.
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler", // or "node16"
"strict": true
}
}