> ## Documentation Index
> Fetch the complete documentation index at: https://docs.matrices.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build your first agent

> A role, a profile, an identity, and a first thread.

The [Quickstart](/quickstart) gave the agent every tool in the workspace. This
time you'll pick the tools yourself, keep the behavior in a
[profile](/model/profiles) other agents can share, and give the agent an
identity you can trace its calls back to.

## 1. Create a role

An agent gets its tools from a [role](/model/roles) and nowhere else. This one
reads the web and keeps notes:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export ROLE_ID="$(
  curl --fail --silent --show-error "https://api.matrices.com/v1/roles" \
    -H "Authorization: Bearer $MATRICES_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Research reader",
      "description": "Reads the web and keeps its own notes.",
      "allow": ["tool:builtin:web.*", "tool:builtin:memory.*"]
    }' | jq -er '.role.id'
)"
```

Every workspace also comes with an `Administrator` role that grants `tool:*`.
That's fine while you're poking around, but too much for an agent you leave
running.

## 2. Create a profile

A [profile](/model/profiles) is everything about how the agent behaves: the
models it may use, the instructions it always carries, and the roles its tools
come from.

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export PROFILE_ID="$(
  curl --fail --silent --show-error "https://api.matrices.com/v1/profiles" \
    -H "Authorization: Bearer $MATRICES_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Research assistant",
      "configuration": {
        "models": ["anthropic/claude-sonnet-5"],
        "roleIds": ["'"$ROLE_ID"'"],
        "systemPrompt": "Answer with sources. Cite every URL you used, and save anything worth remembering with memory.write."
      }
    }' | jq -er '.profile.id'
)"
```

Leave `hooks` out and you get the six default [approvals](/model/hooks).

## 3. Create the agent

The agent is the part that sticks around. It owns the threads and the memory:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export AGENT_ID="$(
  curl --fail --silent --show-error "https://api.matrices.com/v1/agents" \
    -H "Authorization: Bearer $MATRICES_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Research assistant",
      "profileId": "'"$PROFILE_ID"'",
      "identity": "svc:research"
    }' | jq -er '.agent.id'
)"
```

`identity` is required, and you can't change it later. Use a stable value from
your own system: `user_123` for a person, `svc:research` for your product. See
[Identities](/model/identities).

## 4. Start a thread

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export THREAD_ID="$(
  curl --fail --silent --show-error \
    "https://api.matrices.com/v1/agents/$AGENT_ID/threads" \
    -H "Authorization: Bearer $MATRICES_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": [
        { "type": "text", "text": "What changed in the EU AI Act this month? Cite your sources." }
      ]
    }' | jq -er '.threadId'
)"
```

## Check it worked

Ask what the agent can call. This is the same answer Matrices enforces at
run time:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl --fail --silent --show-error -H "Authorization: Bearer $MATRICES_API_KEY" \
  "https://api.matrices.com/v1/agents/$AGENT_ID/effective-permissions" | jq '.builtIns'
```

That should be the `web.*` and `memory.*` tools and nothing else. Now read the
thread's log:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl --fail --silent --show-error -H "Authorization: Bearer $MATRICES_API_KEY" \
  "https://api.matrices.com/v1/threads/$THREAD_ID/events?limit=100" |
  jq -r '.events[] | "\(.seq) \(.type)"'
```

Each tool call shows up as a `tool_intent` followed by its `tool_result`. The
answer is an `assistant_message`. See
[Thread events](/reference/thread-events).

## Where next

<CardGroup cols={2}>
  <Card title="Narrow the permissions" icon="shield" href="/guides/narrow-permissions">
    Conditional allows, denies, and how they stack.
  </Card>

  <Card title="Connect a service" icon="link" href="/guides/connect-a-service">
    Give it credentials the model never sees.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.