Agent

Run your first agent

Create an agent, open a session, send a message, and verify the reply through the API.

Create a service-agent conversation through the API and verify its reply from the event history. This example runs on a server. It needs no web component or embed policy.

Prepare Bun or Node.js with native fetch, a Develop organization API Key, access to deepseek-flash, and available balance. Set PRAXIS_API_KEY in the server process; see API keys. If your Key belongs to another environment, align the API address, organization, and model before running. Do not mix environments.

1. Create an agent

Create first-agent.mjs with the code below. Append the next three JavaScript blocks to the same file, in order; do not run them separately. The assistant answers a simple arithmetic question so you can recognize a completed first call. Add business tools after connecting successfully.

POST /agent/v1/agents creates a configuration from its name, kind, model, and instructions. tools: [] declares no additional tools. The request helper checks each response and applies a timeout. Failures print only the status and request identifier, never the Key.

import assert from "node:assert/strict";
import { setTimeout as delay } from "node:timers/promises";

const { PRAXIS_API_KEY } = process.env;
assert(PRAXIS_API_KEY, "Set PRAXIS_API_KEY in the server process");
const base = "https://api-develop.deepraxis.com/agent/v1";
async function request(path, body) {
  const response = await fetch(`${base}${path}`, {
    method: body === undefined ? "GET" : "POST",
    headers: {
      Authorization: `Bearer ${PRAXIS_API_KEY}`,
      "Content-Type": "application/json",
    },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
    signal: AbortSignal.timeout(30_000),
  });
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}; request=${response.headers.get("X-Request-Id") ?? response.headers.get("request-id")}`);
  }
  return response.json();
}
const agent = await request("/agents", {
  name: "First support agent", kind: "service", model: "deepseek-flash",
  system: "Answer the user's question briefly. Do not use tools for this exercise.",
  tools: [],
});
assert(agent.id.startsWith("agent_"));
console.log({ agent: agent.id, version: agent.version });

Expected result: Creation returns HTTP 200 with a public id and a version. The script prints both and reuses them in later requests. Do not construct IDs yourself.

2. Open a session

Append this code. POST /agent/v1/sessions pins the version you just created. Omitting environment_id selects a light machine; you do not need to create a sandbox first. This example explicitly sets a budget of 100 US cents. That ceiling is not an estimate of the cost of this question.

const session = await request("/sessions", {
  agent: { type: "agent", id: agent.id, version: agent.version },
  title: "First API conversation",
  budget: { type: "limit", max_list_cost: { amount: "100", currency: "USD" } },
});
assert(session.id);
console.log({ session: session.id });

Expected result: HTTP 201 with the session id. No user question has been sent yet, so an idle status cannot prove that a reply exists. The budget controls whether execution may continue; it does not guarantee a particular answer.

3. Send the first message

Append this code to call POST /agent/v1/sessions/{session_id}/events. The events array contains one user.message with exactly one nonempty text block.

const accepted = await request(`/sessions/${session.id}/events`, {
  events: [{ type: "user.message", content: [{ type: "text", text: "What is 2 + 2?" }] }],
});
assert(accepted.data.some(event => event.type === "user.message"));

Expected result: HTTP 200 with the accepted user event in data. This acknowledges the input; it is not the agent's answer. Do not resend the message just because the answer has not appeared yet.

4. Read and verify the reply

Append this code, then run node first-agent.mjs or bun first-agent.mjs. It reads every page from GET /agent/v1/sessions/{session_id}/events, finds an agent.message, and then finds a subsequent session.status_idle. It does not mistake the initial idle state for completion.

let completed = false;
const deadline = Date.now() + 120_000;
while (Date.now() < deadline) {
  const events = [];
  let page;
  do {
    const query = new URLSearchParams({ order: "asc", limit: "100" });
    if (page) query.set("page", page);
    const result = await request(`/sessions/${session.id}/events?${query}`);
    events.push(...result.data);
    page = result.next_page;
  } while (page && Date.now() < deadline);
  const reply = events.findIndex(event => event.type === "agent.message");
  const idle = events.findIndex((event, i) => i > reply && event.type === "session.status_idle");
  if (reply >= 0 && idle > reply) {
    const text = events.filter(event => event.type === "agent.message")
      .flatMap(event => event.content ?? []).filter(block => block.type === "text")
      .map(block => block.text).join("\n");
    assert(text.trim(), "Expected a nonempty reply");
    console.log({ reply: text, completed: true });
    completed = true;
    break;
  }
  await delay(1500);
}
assert(completed, `No completed reply before timeout; inspect session ${session.id}`);

Completion: The process exits successfully and prints a nonempty reply with completed: true. The answer should express 4. The 120-second deadline belongs to this example, not to the service. A nonempty-text assertion alone does not validate business correctness; prepare expected answers for your actual use case before launching.

Troubleshoot failures

SymptomNext action
HTTP 401Check that the server Key exists, remains valid, and belongs to this environment
HTTP 400Check request fields and model availability; see Limits and errors
HTTP 404Use IDs returned by creation and check the organization and pinned version
HTTP 429 or a temporary upstream failureBack off according to the response; inspect the session before creating resources or sending inputs again
No completed reply before timeoutRetain the session ID and inspect events for tool approval, budget, or execution failures; the creation status alone is insufficient

Running the complete script again creates another agent and session. During troubleshooting, query the IDs already printed first. Once you no longer need the test records, manage them through the API reference. A running session cannot be treated as a record ready for deletion.

Next steps

Configure an agent, read Agents and sessions, or add PraxisKit to a web page.

Give this to your agent:

Follow /en/docs/developers/agent/quickstart on the server.
Ask me for a Develop organization API Key and confirm deepseek-flash access.
Complete when a nonempty agent.message is followed by session.status_idle.
Keep the Key out of browsers and logs. Record the agent ID, version, and session ID.
Export

On this page