> For the complete documentation index, see [llms.txt](https://docs.theacompute.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.theacompute.com/integrations/javascript-sdk.md).

# JavaScript and TypeScript SDK

`@theacompute/sdk` is my official typed client. Chat completions are only the beginning. It also gives you wallet-native auth, structured streaming, React hooks, and the on-chain receipt for every job I settle, and you never have to parse a header yourself.

***

## Install me

```bash
npm install @theacompute/sdk
# or
pnpm add @theacompute/sdk
# or
yarn add @theacompute/sdk
```

***

## Your first request

Make an API key in Settings first. I show it only once, so keep it somewhere safe, like the `THEACOMPUTE_API_KEY` environment variable below.

```typescript
import { TheaComputeClient } from "@theacompute/sdk"

const client = new TheaComputeClient({
    apiKey: process.env.THEACOMPUTE_API_KEY
})

// A plain chat completion, no streaming
const response = await client.chat.completions.create({
    model: "qwen3-8b",
    messages: [{ role: "user", content: "What is WebGPU?" }]
})

console.log(response.choices[0].message.content)
console.log("Job ID:", response.jobId)
console.log("Settlement tx:", response.settlementTx)
```

***

## Let me stream it

A quick heads up: my API calls your units "credits", so `creditsCharged` below is the number of units the job cost.

```typescript
const stream = await client.chat.completions.create({
    model: "llama-3.3-70b",
    messages: [{ role: "user", content: "Explain how optimistic rollups work." }],
    stream: true
})

for await (const chunk of stream) {
    process.stdout.write(chunk.choices[0]?.delta?.content ?? "")
}

// When the stream ends, I fill in its receipt
const receipt = stream.receipt
console.log("Settlement:", receipt.settlementTx)
console.log("Worker:", receipt.workerAddress)
console.log("Credits used:", receipt.creditsCharged)
```

***

## My on-chain receipts

I leave a receipt on Robinhood Chain for every job I settle, so you get the record instead of my word for it. Pick it up from the stream object when streaming ends, or look up any past job by its ID.

```typescript
// From a finished stream
const receipt = stream.receipt
// {
//   jobId: "job_8fx2kp3m...",
//   model: "llama-3.3-70b",
//   tier: "max",
//   creditsCharged: 40,
//   usdgValue: 0.40,
//   workerAddress: "0x9e2d5b7a814cf3068d91e4a2b5c60f7d83a41c9e",
//   escrowTx: "0x4b8e2f7c9a1d6053e8b24f9c7a30d1e5f6829c4b7d0a3e8f1c5b9d2a6e470381",
//   settlementTx: "0xd3a91c5e7f2b8064a1c9e5d27b483f0a6e1c8b5d9f2a7043e6b1d8c4a95f7e20",
//   blockNumber: 3218472,
//   proofHash: "sha256:a1b2c3d4..."
// }

// Or ask me for any past job by its ID
const job = await client.jobs.get("job_8fx2kp3m9qrstvwxyz")
console.log(job.onChain.settlementTx)

// Turn the settlement into a Blockscout link
const explorerUrl = `https://robinhoodchain.blockscout.com/tx/${job.onChain.settlementTx}`
```

***

## My React hooks

My React bindings come in the same package. Import them from the `@theacompute/sdk/react` entry point.

```typescript
import { useTheaComputeChat } from "@theacompute/sdk/react"

function ChatComponent() {
    const { send, messages, status, balance, lastReceipt } = useTheaComputeChat({
        model: "qwen3-8b",
        apiKey: process.env.NEXT_PUBLIC_THEACOMPUTE_API_KEY
    })

    const handleSend = async (text: string) => {
        await send(text)
    }

    return (
        <div>
            <p>Credits remaining: {balance?.creditsRemaining}</p>
            <p>Status: {status}</p>

            {messages.map((msg, i) => (
                <div key={i}>
                    <strong>{msg.role}:</strong> {msg.content}
                </div>
            ))}

            {lastReceipt && (
                <p>
                    Last job settled:{" "}
                    <a href={`https://robinhoodchain.blockscout.com/tx/${lastReceipt.settlementTx}`}>
                        View on Blockscout
                    </a>
                </p>
            )}
        </div>
    )
}
```

### What my hook gives you

```typescript
const {
    send,           // (message: string) => Promise<void>
    messages,       // Array<{ role: string; content: string }>
    status,         // "idle" | "streaming" | "settling" | "error"
    balance,        // { creditsRemaining: number; usdgValue: number } | null
    lastReceipt,    // JobReceipt | null
    error,          // Error | null
    reset           // () => void, clears the conversation
} = useTheaComputeChat({
    model: "qwen3-8b",
    apiKey: "thea_live_...",
    systemPrompt: "You are a helpful assistant.",  // optional
    onComplete: (receipt) => { ... },              // optional, I call it when a job finishes
    onError: (error) => { ... }                    // optional, I call it when something breaks
})
```

***

## Your account and units

Ask me for your balance and recent jobs. Fields like `creditsRemaining` count your units. When you run low, you can top up in Settings with **Add more units**.

```typescript
const account = await client.account.get()
// {
//   wallet: "0x7c41f9b8d2a6e3054cf18a9b62d47e0c93f5a1b8",
//   creditsRemaining: 1420,
//   usdgValue: 14.20,
//   lastTopupAt: Date
// }

// Flip through your recent jobs
const jobs = await client.jobs.list({ limit: 10, status: "settled" })
for (const job of jobs.data) {
    console.log(`${job.id}: ${job.model} - ${job.creditsCharged} credits`)
}
```

***

## My webhooks

You set up webhooks in Settings. I sign every event, so check the signature before you trust it. The `credit.low` event means your units are running low.

```typescript
import { verifyWebhookSignature, type TheaComputeWebhookEvent } from "@theacompute/sdk"

// Check my signature, then handle the event in an Express route
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
    const signature = req.headers["theacompute-signature"] as string
    const isValid = verifyWebhookSignature(
        req.body.toString(),
        signature,
        process.env.WEBHOOK_SECRET!
    )

    if (!isValid) {
        return res.status(400).json({ error: "Invalid signature" })
    }

    const event: TheaComputeWebhookEvent = JSON.parse(req.body.toString())

    switch (event.event) {
        case "job.completed":
            console.log("Job settled:", event.data.settlementTx)
            break
        case "credit.low":
            console.log("Credits low:", event.data.creditsRemaining)
            break
    }

    res.json({ received: true })
})
```

***

## Tuning the client

```typescript
const client = new TheaComputeClient({
    apiKey: "thea_live_...",       // required
    baseURL: "https://api.theacompute.com/v1",  // my default; point it elsewhere to test
    timeout: 180_000,                 // timeout per request in ms (default: 120000)
    maxRetries: 3,                    // I retry on 5xx and 503 for you (default: 2)
    defaultHeaders: {                 // added to every request
        "x-app-version": "1.0.0"
    }
})
```

***

## My TypeScript types

These are the types you'll use most. I export them all from `@theacompute/sdk`:

```typescript
import type {
    ChatCompletion,
    ChatCompletionChunk,
    ChatCompletionCreateParams,
    JobReceipt,
    Job,
    Account,
    Model,
    TheaComputeModel,       // a Model, plus tier, workers, and latency
    WebhookEvent,
    JobCompletedEvent,
    CreditLowEvent
} from "@theacompute/sdk"
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.theacompute.com/integrations/javascript-sdk.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
