# Config Model
Source: https://docs.blocks.team/agents/config-model
Beyond the `event` payload argument into your agent, you can also accept a second optional argument: `config`. The `config` should be a [Pydantic](https://docs.pydantic.dev/latest/) model that you define, which can hold configurable state for your agent such as prompts, Sentry project to Github project mappings, and so on.
## Example
```python theme={null}
from blocks import task
from pydantic import BaseModel, Field
class SentryToRepoMapping(BaseModel):
sentry_id: str
repo: str
class Config(BaseModel):
sentry_id_to_repo_map: List[SentryToRepoMapping] = Field(default=[])
prompt: str = Field(default="""
This is an example prompt.
## Directory Structure:
{directory_listing}
## Files Changed:
{files_changed_listing}
""", required=True, required_fields=["directory_listing", "files_changed_listing"])
@agent(name="hello-world")
@on("github.pull_request)
def my_task(event, config: Config):
print(config.prompt)
```
When your agent is registered, we generate a dynamic form from your Pydantic model schema. You can edit the configuration state on the [agent page](https://blocks.team/signup).
# Decorators
Source: https://docs.blocks.team/agents/decorators
Every entrypoint uses an `@on` decorator to bind an event trigger plus one of:
* `@task` — single-turn automations (one-shot per trigger; can still be
long-running). Use this by default.
* `@agent` — multi-turn agents that continue a conversation with the user
across Slack, Linear, or the dashboard.
```python task.py theme={null}
@task(name="my-automation")
@on("schedule.daily")
def task(event):
pass
```
```python agent.py theme={null}
@agent(name="my-agent")
@on("github.pull_request_comment")
def agent(event):
pass
```
The order of the decorators can be interchanged.
See [@task](/decorators/task), [@agent](/decorators/agent), and [@on](/decorators/on)
for the full argument list.
# Environment Variables
Source: https://docs.blocks.team/agents/environment-variables
Environment variables are created and managed in the [dashboard](https://blocks.team/signup). They are injected into an agent's environment at runtime.
## Accessing Environment Variables
Environment variables are accessed in the same way as normal Python scripts. Typically, this is with the `os` module.
```python agent.py theme={null}
import os
@agent(name="my-agent")
def entrypoint(event):
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
```
## Secrets
Secrets are encrypted in storage and during transport. You can create a secret by enabling the secret toggle when creating an environment variable in the dashboard.
## Required Environment Variables
Automation authors can specify `required_env_vars` keys in an agent's [agent decorator](/decorators/agent).
# Events
Source: https://docs.blocks.team/agents/events
Events are what trigger agents, and each event has some unique payload schema which is passed in as an argument into an agent's entry point. For an exhaustive definiton of each event and schema, please refer to the [Events API](/events/github-issue-comment).
```python agent.py theme={null}
@on("github.pull_request_comment")
def agent(event):
files_changed = event.get("files_changed")
```
For our beta release, we currently support a subset of Github, Slack, and Linear webhook events along with basic scheduling and webhook support.
If there is a specific event you'd like to see supported, please reach out to one of our founders: [tomislav@blocksorg.com](mailto:tomislav@blocksorg.com) or [alejandro@blocksorg.com](mailto:alejandro@blocksorg.com).
# configure
Source: https://docs.blocks.team/cli/configure
The `configure` command is used to configure CLI parameters, such as your API key.
Typically, you'd configure your API key upon initialization: `blocks init --key `, but you'll use `configure` if blocks is already initialized and you need to update your key.
```bash bash theme={null}
blocks configure --key
```
You can also use `blocks configure` for an interactive experience.
# create
Source: https://docs.blocks.team/cli/create
The `create` command is used to create a new agent template. This will only create the directory and template files, and will not register the agent.
```bash bash theme={null}
blocks create hello_world
```
This will create a new agent in the `.blocks` directory with the following structure:
```
.blocks/
hello_world/
main.py
requirements.txt
```
You can edit the `main.py` file to define your agent, and add any dependencies to `requirements.txt`. We automatically add `blocks-sdk` as a dependency. You'll want to update the `blocks-sdk` version often to get the latest features and bug fixes.
# init
Source: https://docs.blocks.team/cli/init
The `init` command is used to initialize Blocks in your current working directory. This will create a `.blocks` directory in the current working directory. You can also specify your API key using the `--key` flag, which is obtained from the [Blocks dashboard](https://blocks.team/signup).
```bash bash theme={null}
blocks init --key
```
# push
Source: https://docs.blocks.team/cli/push
The `push` command is used to register an agent. This command will create a new agent if it doesn't already exist, or update an existing agent if it does.
```bash bash theme={null}
blocks push
```
If your folder structured looked like the following:
```
.blocks/
your_agent/
filename.py
```
You'd run:
```bash bash theme={null}
blocks push .blocks/your_agent/filename.py
```
When you change your agent's `pip` or `plugins` dependencies, the command may take a few minutes to complete, since we need to build a runtime for your agent. Subsequent pushes will execute immediately, assuming the dependencies have not changed.
The `name` of an agent is specified in the `agent` decorator. This is used to identify an agent and must be unique per workspace. Changing this will create a new agent.
# @agent
Source: https://docs.blocks.team/decorators/agent
The `@agent` decorator is used to define agent metadata and information about the runtime environment that will be provisioned to execute it.
```python agent.py theme={null}
@agent(
name="my-agent",
required_env_vars=["CLAUDE_API_KEY"]
)
```
The only required argument for the `@agent` decorator is the `name` argument. The `name` must be unique per workspace; changing the `name` will create a new agent or overwrite an existing agent if the `name` already exists.
### Arguments
The name of the agent.
The environment variables that are required to run your agent. Automations cannot be enabled when these are not defined.
# @on
Source: https://docs.blocks.team/decorators/on
The `@on` decorator is used to define the event that will trigger the agent.
```python agent.py theme={null}
@on("github.pull_request")
```
The `@on("event")` decorator will correspond to the `event` payload of your function. See the [events documentation](/events) for more information.
### Arguments
The event that will trigger the agent. Supported values are `github.issues`, `github.issue_comment`, `github.pull_request`, `github.pull_request_review_comment`, `github.pull_request_comment`, `linear.issues`, `linear.issue_comment` `schedule.daily`, `schedule.weekly`, `slack.mention`, `webhook`.
See the [events documentation](/events) for more information on the payloads for each event.
`schedule.daily` and `schedule.weekly` are only valid on `@task` entrypoints.
# @task
Source: https://docs.blocks.team/decorators/task
The `@task` decorator defines a single-turn automation. Tasks are one-shot
(but can be long-running) and can be triggered by events or on a schedule
(`schedule.daily`, `schedule.weekly`).
```python automation.py theme={null}
from blocks import on, task
@on("schedule.daily")
@on("webhook")
@task(name="version-drift-bot")
def version_drift_bot(input):
...
```
The only required argument is `name`. The `name` must be unique per workspace;
changing it will create a new automation or overwrite an existing one with the
same name.
### Arguments
The name of the automation.
Environment variables that must be set before the automation can be enabled.
### When to use `@task` vs `@agent`
Reach for `@task` first. Tasks are one-turn — they run once per trigger and
finish. Use them for scheduled jobs, webhook handlers, or any automation that
doesn't need to keep talking to a user.
Reach for `@agent` only when you need a **multi-turn conversation** — users
continuing the thread from Slack, Linear, or the dashboard, with context
carried across messages.
```python task.py theme={null}
from blocks import on, task
@on("schedule.daily")
@task(name="version-drift-bot")
def version_drift_bot(input):
...
```
```python agent.py theme={null}
from blocks import agent, on
@agent(name="claude-custom", required_env_vars=["ANTHROPIC_API_KEY"])
@on("slack.mention")
@on("github.issue_comment")
def claude_agent(input, config):
...
```
# github.issue_comment
Source: https://docs.blocks.team/events/github-issue-comment
This event occurs when there is a comment on an issue. Github considers `issues` and `pull_requests` to both be issues, but we have separated these events for clarity.
```python theme={null}
@on("github.issue_comment")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"$raw": {},
"action": "created",
"issue": {
"author": "octocat",
"body": "When trying to login with GitHub, the callback URL is not handling special characters correctly.",
"comments": [
{
"author": "other-user",
"body": "I can reproduce this issue. It happens when the username contains '@' symbol.",
"created_at": "2024-03-21T14:35:00Z",
"id": 987654321,
"node_id": "MDEyOklzc3VlQ29tbWVudDk4NzY1NDMyMQ==",
"url": "https://api.github.com/repos/octocat/Hello-World/issues/comments/987654321"
}
],
"created_at": "2024-03-21T14:30:00Z",
"id": 1234567890,
"labels": [
{
"color": "d73a4a",
"default": true,
"description": "Something isn't working",
"id": 123456,
"name": "bug",
"node_id": "MDU6TGFiZWwxMjM0NTY="
},
{
"color": "b60205",
"default": false,
"description": "Needs immediate attention",
"id": 789012,
"name": "high-priority",
"node_id": "MDU6TGFiZWw3ODkwMTI="
}
],
"node_id": "MDU6SXNzdWUxMjM0NTY3ODkw",
"number": 42,
"state": "open",
"title": "Found a bug in the authentication flow",
"url": "https://api.github.com/repos/octocat/Hello-World/issues/42"
},
"new_comment": {
"author": "developer123",
"body": "I've identified the root cause. The URL encoding is being applied twice in the callback handler. Will submit a fix shortly.",
"created_at": "2024-03-21T15:00:00Z",
"id": 987654322,
"node_id": "MDEyOklzc3VlQ29tbWVudDk4NzY1NDMyMg==",
"url": "https://api.github.com/repos/octocat/Hello-World/issues/comments/987654322"
},
"owner": "octocat",
"repo": "Hello-World"
}
```
```python agent.py theme={null}
def agent(input):
$raw = input.get("$raw")
action = input.get("action")
issue = input.get("issue")
new_comment = input.get("new_comment")
owner = input.get("owner")
repo = input.get("repo")
```
## Fields
The raw Github `issue_comment` event payload.
The action that occurred on the pull request.
The pull request was opened.
The pull request was edited. For example, the title or description was changed.
The pull request was synchronized. For example, when a new commit is pushed to the branch.
The issue that the comment is on.
The author of the issue.
The body of the issue.
The comments on the issue.
The author of the comment.
The body of the comment.
The date and time the comment was created.
The ID of the comment.
The node ID of the comment.
The URL of the comment.
The date and time the issue was created.
The ID of the issue.
The labels on the issue.
The color of the label.
Whether the label is the default label.
The description of the label.
The ID of the label.
The name of the label.
The node ID of the label.
The node ID of the issue.
The number of the issue.
The state of the issue.
The title of the issue.
The URL of the issue.
The new comment that was added.
The author of the new comment.
The body of the new comment.
The date and time the new comment was created.
The ID of the new comment.
The node ID of the new comment.
The URL of the new comment.
The owner of the repository.
The name of the repository.
# github.issues
Source: https://docs.blocks.team/events/github-issues
This event occurs when there is activity on a Github issue, such as creation or editing.
```python theme={null}
@on("github.issues")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"$raw": {},
"action": "opened",
"issue": {
"author": "octocat",
"body": "When trying to login with GitHub, the callback URL is not handling special characters correctly.",
"comments": [
{
"author": "other-user",
"body": "I can reproduce this issue. It happens when the username contains '@' symbol.",
"created_at": "2024-03-21T14:35:00Z",
"id": 987654321,
"node_id": "MDEyOklzc3VlQ29tbWVudDk4NzY1NDMyMQ==",
"url": "https://api.github.com/repos/octocat/Hello-World/issues/comments/987654321"
}
],
"created_at": "2024-03-21T14:30:00Z",
"id": 1234567890,
"labels": [
{
"color": "d73a4a",
"default": true,
"description": "Something isn't working",
"id": 123456,
"name": "bug",
"node_id": "MDU6TGFiZWwxMjM0NTY="
},
{
"color": "b60205",
"default": false,
"description": "Needs immediate attention",
"id": 789012,
"name": "high-priority",
"node_id": "MDU6TGFiZWw3ODkwMTI="
}
],
"node_id": "MDU6SXNzdWUxMjM0NTY3ODkw",
"number": 42,
"state": "open",
"title": "Found a bug in the authentication flow",
"url": "https://api.github.com/repos/octocat/Hello-World/issues/42"
},
"owner": "octocat",
"repo": "Hello-World"
}
```
```python agent.py theme={null}
def agent(input):
$raw = input.get("$raw")
action = input.get("action")
issue = input.get("issue")
owner = input.get("owner")
repo = input.get("repo")
```
## Fields
The raw Github `issues` event payload.
The action that occurred on the issue.
The issue was opened.
The issue was edited. For example, the title or description was changed.
The issue that the event is on.
The author of the issue.
The body of the issue.
The comments on the issue.
The author of the comment.
The body of the comment.
The date and time the comment was created.
The ID of the comment.
The node ID of the comment.
The URL of the comment.
The date and time the issue was created.
The ID of the issue.
The labels on the issue.
The color of the label.
Whether the label is the default label.
The description of the label.
The ID of the label.
The name of the label.
The node ID of the label.
The node ID of the issue.
The number of the issue.
The state of the issue.
The title of the issue.
The URL of the issue.
The owner of the repository.
The name of the repository.
# github.pull_request
Source: https://docs.blocks.team/events/github-pull-request
This event occurs when there is activity on a pull request.
```python theme={null}
@on("github.pull_request")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
````json json theme={null}
{
"$raw": {},
"action": "opened",
"author": "developer123",
"changes": [
{
"additions": 25,
"changes": 35,
"deletions": 10,
"filename": "src/services/auth.ts",
"patch": "@@ -15,10 +15,25 @@ export class AuthService {\n private async validateToken(token: string) {\n- // Basic auth validation\n- const [username, password] = Buffer.from(token, 'base64')\n- .toString()\n- .split(':');\n-\n- return this.validateCredentials(username, password);\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;\n+ \n+ if (!decoded.sub || !decoded.exp) {\n+ throw new Error('Invalid token structure');\n+ }\n+ \n+ if (decoded.exp < Date.now() / 1000) {\n+ throw new Error('Token has expired');\n+ }\n+ \n+ const user = await this.userService.findById(decoded.sub);\n+ if (!user) {\n+ throw new Error('User not found');\n+ }\n+ \n+ if (!user.active) {\n+ throw new Error('User account is disabled');\n+ }\n+ \n+ return user;\n+ } catch (error) {\n+ throw new AuthenticationError('Invalid or expired token');\n+ }\n }",
"status": "modified"
},
{
"additions": 50,
"changes": 50,
"deletions": 0,
"filename": "tests/auth.test.ts",
"patch": "@@ -0,0 +1,50 @@\n+import { describe, it, expect, jest } from 'jest';\n+import { AuthService } from '../src/services/auth';\n+import { UserService } from '../src/services/user';\n+import { AuthenticationError } from '../src/errors';\n+\n+describe('AuthService', () => {\n+ let authService: AuthService;\n+ let userService: jest.Mocked;\n+\n+ beforeEach(() => {\n+ userService = {\n+ findById: jest.fn(),\n+ } as any;\n+\n+ authService = new AuthService({\n+ jwt_secret: 'test-secret',\n+ token_expiry: '1h'\n+ }, userService);\n+ });\n+\n+ describe('validateToken', () => {\n+ it('should successfully validate a valid token', async () => {\n+ const mockUser = {\n+ id: '123',\n+ active: true,\n+ name: 'Test User'\n+ };\n+\n+ userService.findById.mockResolvedValue(mockUser);\n+\n+ const token = authService.generateToken(mockUser);\n+ const result = await authService.validateToken(token);\n+\n+ expect(result).toEqual(mockUser);\n+ expect(userService.findById).toHaveBeenCalledWith('123');\n+ });\n+\n+ it('should throw on expired token', async () => {\n+ const mockUser = {\n+ id: '123',\n+ active: true,\n+ name: 'Test User'\n+ };\n+\n+ // Generate token that's already expired\n+ const token = authService.generateToken(mockUser, '-1h');\n+\n+ await expect(authService.validateToken(token))\n+ .rejects\n+ .toThrow(AuthenticationError);\n+ });\n+ });\n+});",
"status": "added"
},
{
"additions": 15,
"changes": 20,
"deletions": 5,
"filename": "README.md",
"patch": "@@ -10,8 +10,18 @@ ## Authentication\n-### Basic Authentication\n-To authenticate requests, provide your credentials in the Authorization header:\n-\n-```\n-Authorization: Basic base64(username:password)\n-```\n+### OAuth2 Authentication\n+\n+This API uses OAuth2 for authentication. To authenticate requests, include a Bearer token in the Authorization header:\n+\n+```\n+Authorization: Bearer \n+```\n+\n+#### Obtaining a Token\n+\n+1. Redirect users to `/oauth/authorize`\n+2. Users approve the application\n+3. Exchange the authorization code for a token at `/oauth/token`\n+\n+Tokens expire after 1 hour. Use the refresh token to obtain a new access token.\n+\n+For more details, see our [OAuth2 Implementation Guide](./docs/oauth2.md).",
"status": "modified"
}
],
"commit_message": "feat: implement OAuth2 authentication",
"commit_sha": "abc123def456789ghijklmnop",
"files_changed": [
"src/services/auth.ts",
"tests/auth.test.ts",
"README.md"
],
"owner": "octocat",
"pull_request": {
"author": "developer123",
"body": "This PR adds OAuth2 authentication support and removes the basic auth implementation.\n\nChanges:\n- Replaces basic auth with JWT-based OAuth2\n- Adds comprehensive tests\n- Updates documentation\n- Adds token validation and expiry handling\n\nPlease review the token expiry duration and error messages.",
"comments": [
{
"author": "reviewer456",
"body": "Could you add some documentation about token refresh flows? Particularly around handling expired refresh tokens.",
"created_at": "2024-03-21T11:30:00Z",
"diff_hunk": "@@ -15,7 +15,12 @@ export class AuthService {\n private async validateToken(token: string) {\n- // Basic auth validation\n- const [username, password] = Buffer.from(token, 'base64')\n- .toString()\n- .split(':');\n-\n- return this.validateCredentials(username, password);\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;",
"filename": "src/services/auth.ts",
"id": 444555666,
"line": 17,
"node_id": "PRRC_kwDOA3333333333",
"original_line": 2,
"original_position": 2,
"original_start_line": 15,
"start_line": 17,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555666"
},
{
"author": "security-reviewer",
"body": "We should add a check for token blacklisting here in case we need to revoke access.",
"created_at": "2024-03-21T12:15:00Z",
"diff_hunk": "@@ -20,6 +20,11 @@ export class AuthService {\n if (!decoded.sub || !decoded.exp) {\n throw new Error('Invalid token structure');\n }\n+ \n+ if (decoded.exp < Date.now() / 1000) {\n+ throw new Error('Token has expired');\n+ }\n+ \n+ const user = await this.userService.findById(decoded.sub);",
"filename": "src/services/auth.ts",
"id": 444555667,
"line": 25,
"node_id": "PRRC_kwDOA3333333334",
"original_line": null,
"original_position": 5,
"original_start_line": null,
"start_line": 25,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555667"
},
{
"author": "developer123",
"body": "Good catch! I'll add the blacklist check in the next commit.",
"created_at": "2024-03-21T12:30:00Z",
"diff_hunk": "@@ -25,6 +25,10 @@ export class AuthService {\n if (decoded.exp < Date.now() / 1000) {\n throw new Error('Token has expired');\n }\n+ \n+ const user = await this.userService.findById(decoded.sub);\n+ if (!user) {\n+ throw new Error('User not found');\n+ }",
"filename": "src/services/auth.ts",
"id": 444555668,
"line": 28,
"node_id": "PRRC_kwDOA3333333335",
"original_line": null,
"original_position": 8,
"original_start_line": null,
"start_line": 28,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555668"
}
],
"created_at": "2024-03-21T10:00:00Z",
"id": 987654321,
"labels": [
{
"color": "84b6eb",
"default": true,
"description": "New feature or request",
"id": 111222,
"name": "enhancement",
"node_id": "LA_kwDOA1111111111"
},
{
"color": "d93f0b",
"default": false,
"description": "Security related changes",
"id": 333444,
"name": "security",
"node_id": "LA_kwDOA2222222222"
}
],
"node_id": "PR_kwDOA1234567890",
"number": 123,
"state": "open",
"title": "Implement OAuth2 Authentication",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/123"
},
"ref": "feature/oauth-auth",
"repo": "Hello-World"
}
````
```python agent.py theme={null}
def agent(input):
$raw = input.get("$raw")
action = input.get("action")
author = input.get("author")
changes = input.get("changes")
commit_message = input.get("commit_message")
commit_sha = input.get("commit_sha")
files_changed = input.get("files_changed")
owner = input.get("owner")
pull_request = input.get("pull_request")
ref = input.get("ref")
repo = input.get("repo")
```
## Fields
The raw Github `pull_request` event payload.
The action that occurred on the pull request.
The pull request was opened.
The pull request was edited. For example, the title or description was changed.
The pull request was synchronized. For example, a new commit was pushed to the branch.
The author of the pull request.
The changes that were made to the files in the pull request.
The number of additions in the change.
The number of changes in the change.
The number of deletions in the change.
The filename of the file that the change is on.
The patch of the change.
The status of the change.
The commit message of the pull request.
The SHA of the commit that was used to create the pull request.
The files that were changed in the pull request.
The owner of the repository.
The pull request object.
The author of the pull request.
The body of the pull request.
The comments on the pull request.
The author of the comment.
The body of the comment.
The date and time the comment was created.
The diff hunk of the comment.
The filename of the file that the comment is on.
The ID of the comment.
The line number of the comment.
The node ID of the comment.
The original line number of the comment.
The original position of the comment.
The original start line of the comment.
The start line of the comment.
The start side of the comment.
The subject type of the comment.
The URL of the comment.
The date and time the pull request was created.
The ID of the pull request.
The labels on the pull request.
The color of the label.
Whether the label is the default label.
The description of the label.
The ID of the label.
The name of the label.
The node ID of the label.
The node ID of the pull request.
The number of the pull request.
The state of the pull request.
The title of the pull request.
The URL of the pull request.
The ref of the pull request.
The repository of the pull request.
# github.pull_request_comment
Source: https://docs.blocks.team/events/github-pull-request-comment
This event occurs when there is a comment on a pull request.
```python theme={null}
@on("github.pull_request_comment")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
````json json theme={null}
{
"$raw": {},
"action": "created",
"author": "developer123",
"changes": [
{
"additions": 25,
"changes": 35,
"deletions": 10,
"filename": "src/services/auth.ts",
"patch": "@@ -15,10 +15,25 @@ export class AuthService {\n private async validateToken(token: string) {\n- // Basic auth validation\n- const [username, password] = Buffer.from(token, 'base64')\n- .toString()\n- .split(':');\n-\n- return this.validateCredentials(username, password);\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;\n+ \n+ if (!decoded.sub || !decoded.exp) {\n+ throw new Error('Invalid token structure');\n+ }\n+ \n+ if (decoded.exp < Date.now() / 1000) {\n+ throw new Error('Token has expired');\n+ }\n+ \n+ const user = await this.userService.findById(decoded.sub);\n+ if (!user) {\n+ throw new Error('User not found');\n+ }\n+ \n+ if (!user.active) {\n+ throw new Error('User account is disabled');\n+ }\n+ \n+ return user;\n+ } catch (error) {\n+ throw new AuthenticationError('Invalid or expired token');\n+ }\n }",
"status": "modified"
},
{
"additions": 50,
"changes": 50,
"deletions": 0,
"filename": "tests/auth.test.ts",
"patch": "@@ -0,0 +1,50 @@\n+import { describe, it, expect, jest } from 'jest';\n+import { AuthService } from '../src/services/auth';\n+import { UserService } from '../src/services/user';\n+import { AuthenticationError } from '../src/errors';\n+\n+describe('AuthService', () => {\n+ let authService: AuthService;\n+ let userService: jest.Mocked;\n+\n+ beforeEach(() => {\n+ userService = {\n+ findById: jest.fn(),\n+ } as any;\n+\n+ authService = new AuthService({\n+ jwt_secret: 'test-secret',\n+ token_expiry: '1h'\n+ }, userService);\n+ });\n+\n+ describe('validateToken', () => {\n+ it('should successfully validate a valid token', async () => {\n+ const mockUser = {\n+ id: '123',\n+ active: true,\n+ name: 'Test User'\n+ };\n+\n+ userService.findById.mockResolvedValue(mockUser);\n+\n+ const token = authService.generateToken(mockUser);\n+ const result = await authService.validateToken(token);\n+\n+ expect(result).toEqual(mockUser);\n+ expect(userService.findById).toHaveBeenCalledWith('123');\n+ });\n+\n+ it('should throw on expired token', async () => {\n+ const mockUser = {\n+ id: '123',\n+ active: true,\n+ name: 'Test User'\n+ };\n+\n+ // Generate token that's already expired\n+ const token = authService.generateToken(mockUser, '-1h');\n+\n+ await expect(authService.validateToken(token))\n+ .rejects\n+ .toThrow(AuthenticationError);\n+ });\n+ });\n+});",
"status": "added"
},
{
"additions": 15,
"changes": 20,
"deletions": 5,
"filename": "README.md",
"patch": "@@ -10,8 +10,18 @@ ## Authentication\n-### Basic Authentication\n-To authenticate requests, provide your credentials in the Authorization header:\n-\n-```\n-Authorization: Basic base64(username:password)\n-```\n+### OAuth2 Authentication\n+\n+This API uses OAuth2 for authentication. To authenticate requests, include a Bearer token in the Authorization header:\n+\n+```\n+Authorization: Bearer \n+```\n+\n+#### Obtaining a Token\n+\n+1. Redirect users to `/oauth/authorize`\n+2. Users approve the application\n+3. Exchange the authorization code for a token at `/oauth/token`\n+\n+Tokens expire after 1 hour. Use the refresh token to obtain a new access token.\n+\n+For more details, see our [OAuth2 Implementation Guide](./docs/oauth2.md).",
"status": "modified"
}
],
"commit_message": "feat: implement OAuth2 authentication",
"commit_sha": "abc123def456789ghijklmnop",
"files_changed": [
"src/services/auth.ts",
"tests/auth.test.ts",
"README.md"
],
"new_comment": {
"author": "security-expert",
"body": "We should consider adding rate limiting here to prevent brute force attacks on the token validation.",
"created_at": "2024-03-21T16:20:00Z",
"diff_hunk": "@@ -15,10 +15,25 @@ export class AuthService {\n private async validateToken(token: string) {\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;",
"filename": "src/services/auth.ts",
"id": 777888999,
"line": 17,
"node_id": "PRRC_kwDOA5555555555",
"original_line": 2,
"original_position": 2,
"original_start_line": 16,
"start_line": 16,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/123/comments/777888999"
},
"owner": "octocat",
"pull_request": {
"author": "developer123",
"body": "This PR adds OAuth2 authentication support and removes the basic auth implementation.\n\nChanges:\n- Replaces basic auth with JWT-based OAuth2\n- Adds comprehensive tests\n- Updates documentation\n- Adds token validation and expiry handling\n\nPlease review the token expiry duration and error messages.",
"comments": [
{
"author": "reviewer456",
"body": "Could you add some documentation about token refresh flows? Particularly around handling expired refresh tokens.",
"created_at": "2024-03-21T11:30:00Z",
"diff_hunk": "@@ -15,7 +15,12 @@ export class AuthService {\n private async validateToken(token: string) {\n- // Basic auth validation\n- const [username, password] = Buffer.from(token, 'base64')\n- .toString()\n- .split(':');\n-\n- return this.validateCredentials(username, password);\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;",
"filename": "src/services/auth.ts",
"id": 444555666,
"line": 17,
"node_id": "PRRC_kwDOA3333333333",
"original_line": 2,
"original_position": 2,
"original_start_line": 15,
"start_line": 17,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555666"
},
{
"author": "security-reviewer",
"body": "We should add a check for token blacklisting here in case we need to revoke access.",
"created_at": "2024-03-21T12:15:00Z",
"diff_hunk": "@@ -20,6 +20,11 @@ export class AuthService {\n if (!decoded.sub || !decoded.exp) {\n throw new Error('Invalid token structure');\n }\n+ \n+ if (decoded.exp < Date.now() / 1000) {\n+ throw new Error('Token has expired');\n+ }\n+ \n+ const user = await this.userService.findById(decoded.sub);",
"filename": "src/services/auth.ts",
"id": 444555667,
"line": 25,
"node_id": "PRRC_kwDOA3333333334",
"original_line": null,
"original_position": 5,
"original_start_line": null,
"start_line": 25,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555667"
},
{
"author": "developer123",
"body": "Good catch! I'll add the blacklist check in the next commit.",
"created_at": "2024-03-21T12:30:00Z",
"diff_hunk": "@@ -25,6 +25,10 @@ export class AuthService {\n if (decoded.exp < Date.now() / 1000) {\n throw new Error('Token has expired');\n }\n+ \n+ const user = await this.userService.findById(decoded.sub);\n+ if (!user) {\n+ throw new Error('User not found');\n+ }",
"filename": "src/services/auth.ts",
"id": 444555668,
"line": 28,
"node_id": "PRRC_kwDOA3333333335",
"original_line": null,
"original_position": 8,
"original_start_line": null,
"start_line": 28,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555668"
}
],
"created_at": "2024-03-21T10:00:00Z",
"id": 987654321,
"labels": [
{
"color": "84b6eb",
"default": true,
"description": "New feature or request",
"id": 111222,
"name": "enhancement",
"node_id": "LA_kwDOA1111111111"
},
{
"color": "d93f0b",
"default": false,
"description": "Security related changes",
"id": 333444,
"name": "security",
"node_id": "LA_kwDOA2222222222"
}
],
"node_id": "PR_kwDOA1234567890",
"number": 123,
"state": "open",
"title": "Implement OAuth2 Authentication",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/123"
},
"ref": "feature/oauth-auth",
"repo": "Hello-World"
}
````
```python agent.py theme={null}
def agent(input):
$raw = input.get("$raw")
action = input.get("action")
author = input.get("author")
changes = input.get("changes")
commit_message = input.get("commit_message")
commit_sha = input.get("commit_sha")
files_changed = input.get("files_changed")
new_comment = input.get("new_comment")
owner = input.get("owner")
pull_request = input.get("pull_request")
ref = input.get("ref")
repo = input.get("repo")
```
## Fields
The raw Github `pull_request_review_comment` event payload.
The action that occurred on the pull request.
The pull request was opened.
The pull request was edited. For example, the title or description was changed.
The pull request was synchronized. For example, when a new commit is pushed to the branch.
The author of the pull request.
The changes that were made to the files in the pull request.
The number of additions in the change.
The number of changes in the change.
The number of deletions in the change.
The filename of the changed file.
The patch (or code) of the change.
The status of the change.
The commit message of the pull request.
The SHA of the commit that was used to create the pull request.
The filepaths that were changed in the pull request.
The new comment that was added to the pull request.
The author of the comment.
The body of the comment.
The date and time the comment was created.
The diff hunk of the comment.
The filename of the file that the comment was added to.
The ID of the comment.
The node ID of the comment.
The original line number of the comment.
The original position of the comment.
The original start line of the comment.
The start line of the comment.
The start side of the comment.
The subject type of the comment.
The URL of the comment.
The owner of the repository.
The pull request that the comment occurred on.
The author of the pull request.
The body of the pull request.
The comments on the pull request.
The author of the comment.
The body of the comment.
The date and time the comment was created.
The diff hunk of the comment.
The filename of the file that the comment was added to.
The ID of the comment.
The node ID of the comment.
The original line number of the comment.
The original position of the comment.
The original start line of the comment.
The start line of the comment.
The start side of the comment.
The subject type of the comment.
The URL of the comment.
The date and time the pull request was created.
The ID of the pull request.
The labels on the pull request.
The color of the label.
Whether the label is the default label.
The description of the label.
The ID of the label.
The name of the label.
The node ID of the label.
The node ID of the pull request.
The number of the pull request.
The state of the pull request.
The title of the pull request.
The URL of the pull request.
The ref of the pull request.
The repository of the pull request.
# github.pull_request_review_comment
Source: https://docs.blocks.team/events/github-pull-request-review-comment
This event occurs when there is a comment on a pull request.
```python theme={null}
@on("github.pull_request_review_comment")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
````json json theme={null}
{
"$raw": {},
"action": "created",
"author": "developer123",
"changes": [
{
"additions": 25,
"changes": 35,
"deletions": 10,
"filename": "src/services/auth.ts",
"patch": "@@ -15,10 +15,25 @@ export class AuthService {\n private async validateToken(token: string) {\n- // Basic auth validation\n- const [username, password] = Buffer.from(token, 'base64')\n- .toString()\n- .split(':');\n-\n- return this.validateCredentials(username, password);\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;\n+ \n+ if (!decoded.sub || !decoded.exp) {\n+ throw new Error('Invalid token structure');\n+ }\n+ \n+ if (decoded.exp < Date.now() / 1000) {\n+ throw new Error('Token has expired');\n+ }\n+ \n+ const user = await this.userService.findById(decoded.sub);\n+ if (!user) {\n+ throw new Error('User not found');\n+ }\n+ \n+ if (!user.active) {\n+ throw new Error('User account is disabled');\n+ }\n+ \n+ return user;\n+ } catch (error) {\n+ throw new AuthenticationError('Invalid or expired token');\n+ }\n }",
"status": "modified"
},
{
"additions": 50,
"changes": 50,
"deletions": 0,
"filename": "tests/auth.test.ts",
"patch": "@@ -0,0 +1,50 @@\n+import { describe, it, expect, jest } from 'jest';\n+import { AuthService } from '../src/services/auth';\n+import { UserService } from '../src/services/user';\n+import { AuthenticationError } from '../src/errors';\n+\n+describe('AuthService', () => {\n+ let authService: AuthService;\n+ let userService: jest.Mocked;\n+\n+ beforeEach(() => {\n+ userService = {\n+ findById: jest.fn(),\n+ } as any;\n+\n+ authService = new AuthService({\n+ jwt_secret: 'test-secret',\n+ token_expiry: '1h'\n+ }, userService);\n+ });\n+\n+ describe('validateToken', () => {\n+ it('should successfully validate a valid token', async () => {\n+ const mockUser = {\n+ id: '123',\n+ active: true,\n+ name: 'Test User'\n+ };\n+\n+ userService.findById.mockResolvedValue(mockUser);\n+\n+ const token = authService.generateToken(mockUser);\n+ const result = await authService.validateToken(token);\n+\n+ expect(result).toEqual(mockUser);\n+ expect(userService.findById).toHaveBeenCalledWith('123');\n+ });\n+\n+ it('should throw on expired token', async () => {\n+ const mockUser = {\n+ id: '123',\n+ active: true,\n+ name: 'Test User'\n+ };\n+\n+ // Generate token that's already expired\n+ const token = authService.generateToken(mockUser, '-1h');\n+\n+ await expect(authService.validateToken(token))\n+ .rejects\n+ .toThrow(AuthenticationError);\n+ });\n+ });\n+});",
"status": "added"
},
{
"additions": 15,
"changes": 20,
"deletions": 5,
"filename": "README.md",
"patch": "@@ -10,8 +10,18 @@ ## Authentication\n-### Basic Authentication\n-To authenticate requests, provide your credentials in the Authorization header:\n-\n-```\n-Authorization: Basic base64(username:password)\n-```\n+### OAuth2 Authentication\n+\n+This API uses OAuth2 for authentication. To authenticate requests, include a Bearer token in the Authorization header:\n+\n+```\n+Authorization: Bearer \n+```\n+\n+#### Obtaining a Token\n+\n+1. Redirect users to `/oauth/authorize`\n+2. Users approve the application\n+3. Exchange the authorization code for a token at `/oauth/token`\n+\n+Tokens expire after 1 hour. Use the refresh token to obtain a new access token.\n+\n+For more details, see our [OAuth2 Implementation Guide](./docs/oauth2.md).",
"status": "modified"
}
],
"commit_message": "feat: implement OAuth2 authentication",
"commit_sha": "abc123def456789ghijklmnop",
"files_changed": [
"src/services/auth.ts",
"tests/auth.test.ts",
"README.md"
],
"new_comment": {
"author": "security-expert",
"body": "We should consider adding rate limiting here to prevent brute force attacks on the token validation.",
"created_at": "2024-03-21T16:20:00Z",
"diff_hunk": "@@ -15,10 +15,25 @@ export class AuthService {\n private async validateToken(token: string) {\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;",
"filename": "src/services/auth.ts",
"id": 777888999,
"line": 17,
"node_id": "PRRC_kwDOA5555555555",
"original_line": 2,
"original_position": 2,
"original_start_line": 16,
"start_line": 16,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/123/comments/777888999"
},
"owner": "octocat",
"pull_request": {
"author": "developer123",
"body": "This PR adds OAuth2 authentication support and removes the basic auth implementation.\n\nChanges:\n- Replaces basic auth with JWT-based OAuth2\n- Adds comprehensive tests\n- Updates documentation\n- Adds token validation and expiry handling\n\nPlease review the token expiry duration and error messages.",
"comments": [
{
"author": "reviewer456",
"body": "Could you add some documentation about token refresh flows? Particularly around handling expired refresh tokens.",
"created_at": "2024-03-21T11:30:00Z",
"diff_hunk": "@@ -15,7 +15,12 @@ export class AuthService {\n private async validateToken(token: string) {\n- // Basic auth validation\n- const [username, password] = Buffer.from(token, 'base64')\n- .toString()\n- .split(':');\n-\n- return this.validateCredentials(username, password);\n+ try {\n+ const decoded = jwt.verify(token, this.config.jwt_secret) as JwtPayload;",
"filename": "src/services/auth.ts",
"id": 444555666,
"line": 17,
"node_id": "PRRC_kwDOA3333333333",
"original_line": 2,
"original_position": 2,
"original_start_line": 15,
"start_line": 17,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555666"
},
{
"author": "security-reviewer",
"body": "We should add a check for token blacklisting here in case we need to revoke access.",
"created_at": "2024-03-21T12:15:00Z",
"diff_hunk": "@@ -20,6 +20,11 @@ export class AuthService {\n if (!decoded.sub || !decoded.exp) {\n throw new Error('Invalid token structure');\n }\n+ \n+ if (decoded.exp < Date.now() / 1000) {\n+ throw new Error('Token has expired');\n+ }\n+ \n+ const user = await this.userService.findById(decoded.sub);",
"filename": "src/services/auth.ts",
"id": 444555667,
"line": 25,
"node_id": "PRRC_kwDOA3333333334",
"original_line": null,
"original_position": 5,
"original_start_line": null,
"start_line": 25,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555667"
},
{
"author": "developer123",
"body": "Good catch! I'll add the blacklist check in the next commit.",
"created_at": "2024-03-21T12:30:00Z",
"diff_hunk": "@@ -25,6 +25,10 @@ export class AuthService {\n if (decoded.exp < Date.now() / 1000) {\n throw new Error('Token has expired');\n }\n+ \n+ const user = await this.userService.findById(decoded.sub);\n+ if (!user) {\n+ throw new Error('User not found');\n+ }",
"filename": "src/services/auth.ts",
"id": 444555668,
"line": 28,
"node_id": "PRRC_kwDOA3333333335",
"original_line": null,
"original_position": 8,
"original_start_line": null,
"start_line": 28,
"start_side": "RIGHT",
"subject_type": "line",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments/444555668"
}
],
"created_at": "2024-03-21T10:00:00Z",
"id": 987654321,
"labels": [
{
"color": "84b6eb",
"default": true,
"description": "New feature or request",
"id": 111222,
"name": "enhancement",
"node_id": "LA_kwDOA1111111111"
},
{
"color": "d93f0b",
"default": false,
"description": "Security related changes",
"id": 333444,
"name": "security",
"node_id": "LA_kwDOA2222222222"
}
],
"node_id": "PR_kwDOA1234567890",
"number": 123,
"state": "open",
"title": "Implement OAuth2 Authentication",
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/123"
},
"ref": "feature/oauth-auth",
"repo": "Hello-World"
}
````
```python agent.py theme={null}
def agent(input):
$raw = input.get("$raw")
action = input.get("action")
author = input.get("author")
changes = input.get("changes")
commit_message = input.get("commit_message")
commit_sha = input.get("commit_sha")
files_changed = input.get("files_changed")
new_comment = input.get("new_comment")
owner = input.get("owner")
pull_request = input.get("pull_request")
ref = input.get("ref")
repo = input.get("repo")
```
## Fields
The raw Github `pull_request_review_comment` event payload.
The action that occurred on the pull request.
The pull request was opened.
The pull request was edited. For example, the title or description was changed.
The pull request was synchronized. For example, when a new commit is pushed to the branch.
The author of the pull request.
The changes that were made to the files in the pull request.
The number of additions in the change.
The number of changes in the change.
The number of deletions in the change.
The filename of the changed file.
The patch (or code) of the change.
The status of the change.
The commit message of the pull request.
The SHA of the commit that was used to create the pull request.
The filepaths that were changed in the pull request.
The new comment that was added to the pull request.
The author of the comment.
The body of the comment.
The date and time the comment was created.
The diff hunk of the comment.
The filename of the file that the comment was added to.
The ID of the comment.
The node ID of the comment.
The original line number of the comment.
The original position of the comment.
The original start line of the comment.
The start line of the comment.
The start side of the comment.
The subject type of the comment.
The URL of the comment.
The owner of the repository.
The pull request that the comment occurred on.
The author of the pull request.
The body of the pull request.
The comments on the pull request.
The author of the comment.
The body of the comment.
The date and time the comment was created.
The diff hunk of the comment.
The filename of the file that the comment was added to.
The ID of the comment.
The node ID of the comment.
The original line number of the comment.
The original position of the comment.
The original start line of the comment.
The start line of the comment.
The start side of the comment.
The subject type of the comment.
The URL of the comment.
The date and time the pull request was created.
The ID of the pull request.
The labels on the pull request.
The color of the label.
Whether the label is the default label.
The description of the label.
The ID of the label.
The name of the label.
The node ID of the label.
The node ID of the pull request.
The number of the pull request.
The state of the pull request.
The title of the pull request.
The URL of the pull request.
The ref of the pull request.
The repository of the pull request.
# linear.issue_comment
Source: https://docs.blocks.team/events/linear-issue-comment
```python theme={null}
@on("linear.issue_comment")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"action": "create",
"issue": {
"id": "12345678-1234-1234-1234-123456789012",
"number": 13,
"title": "Support multiple agents",
"description": "When I mention Blocks on slack like `@BlocksOrg /codex /gemini do xyz` \n\nWe want to be able to resolve multiple agents during the Trigger resolution service, and furthermore we want to ensure that we submit independent tasks for each (note the `is_allowed` and task session \"grouping\" logic w.r.t to uniq values / session URNs)\n\n> This might already be in place but want to double check this is the case ",
"priority": 0,
"priorityLabel": "No priority",
"state": {
"id": "abcd1234-5678-9012-3456-abcdef123456",
"name": "Todo",
"type": "unstarted",
"color": "#e2e2e2"
},
"team": {
"id": "team1234-5678-9012-3456-123456789012",
"key": "TEAM",
"name": "Example Team"
},
"assignee": undefined,
"creator": {
"id": "user1234-5678-9012-3456-123456789012",
"name": "user@example.com",
"email": "user@example.com"
},
"labels": [],
"url": "https://linear.app/example-org/issue/TEAM-13/support-multiple-agents",
"identifier": "TEAM-13",
"createdAt": "2025-06-04T00:28:24.140Z",
"updatedAt": "2025-09-10T01:35:32.510Z"
},
"organization": {
"id": "org12345-1234-1234-1234-123456789012"
},
"$raw": {
"action": "create",
"createdAt": "2025-09-10T01:35:32.510Z",
"data": {
"id": "comment1-2345-6789-0123-456789012345",
"createdAt": "2025-09-10T01:35:32.519Z",
"updatedAt": "2025-09-10T01:35:32.510Z",
"body": "@blocks can you look into this?",
"issueId": "12345678-1234-1234-1234-123456789012",
"userId": "user1234-5678-9012-3456-123456789012",
"reactionData": [],
"botActor": null,
"user": {
"id": "user1234-5678-9012-3456-123456789012",
"name": "user@example.com",
"email": "user@example.com",
"url": "https://linear.app/example-org/profiles/user"
},
"issue": {
"id": "12345678-1234-1234-1234-123456789012",
"title": "Support multiple agents",
"teamId": "team1234-5678-9012-3456-123456789012",
"team": {
"id": "team1234-5678-9012-3456-123456789012",
"key": "TEAM",
"name": "Example Team"
},
"identifier": "TEAM-13",
"url": "https://linear.app/example-org/issue/TEAM-13/support-multiple-agents"
}
},
"url": "https://linear.app/example-org/issue/TEAM-13/support-multiple-agents#comment-comment1",
"type": "Comment",
"organizationId": "org12345-1234-1234-1234-123456789012"
},
"new_comment": {
"id": "comment1-2345-6789-0123-456789012345",
"body": "@blocks can you look into this?",
"user": {
"id": "user1234-5678-9012-3456-123456789012",
"name": "user@example.com",
"email": "user@example.com"
},
"issue": {
"id": "12345678-1234-1234-1234-123456789012",
"identifier": "TEAM-13",
"title": "Support multiple agents"
},
"createdAt": "2025-09-10T01:35:32.519Z",
"updatedAt": "2025-09-10T01:35:32.510Z"
}
}
```
```python agent.py theme={null}
def agent(input):
action = input.get("action")
issue = input.get("issue")
organization = input.get("organization")
raw_data = input.get("$raw")
new_comment = input.get("new_comment")
```
## Fields
The action that occurred on the issue comment.
A comment was created on the issue.
The issue that the event is on.
The id of the issue.
The number of the issue.
The title of the issue.
The description of the issue.
The priority of the issue.
The label for the priority.
The issue state.
The id of the state.
The name of the state.
The type of the state.
The color of the state.
The issue team.
The id of the team.
The key of the team.
The name of the team.
The assigned user for the issue. May be undefined if no one is assigned.
The issue creator.
The id of the creator.
The name of the creator.
The email of the creator.
The labels attached to the issue.
The url of the issue.
The identifier of the issue.
The date/time the issue was created.
The date/time the issue was updated.
The organization associated with the Linear project.
The Linear organization id.
Raw webhook data from Linear containing additional metadata.
The action that triggered the webhook.
When the webhook event was created.
Direct URL to the comment.
The type of Linear object (e.g., "Comment").
The organization ID.
Detailed comment data from Linear.
The comment that was created on the issue.
The id of the comment.
The content/body of the comment.
When the comment was created.
When the comment was last updated.
The user who created the comment.
The id of the user.
The name of the user.
The email of the user.
Basic issue information for the comment.
The id of the issue.
The identifier of the issue (e.g., "BLO-13").
The title of the issue.
# linear.issues
Source: https://docs.blocks.team/events/linear-issues
This event occurs when there is activity on a Linear issue, such as creation, editing, or state changes.
```python theme={null}
@on("linear.issues")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"action": "create",
"issue": {
"id": "47d4ba46-0f09-4216-97d8-350bb0bfc99f",
"number": 14,
"title": "Remove secrets from logs",
"description": "During log processing, the same way we remove \"/#/\" log lines, we need to remove blocks secrets that are registered. Use the global and local environment variable / secret fetching logic we have and only mask secrets.",
"priority": 0,
"priorityLabel": "No priority",
"state": {
"id": "bfce67a7-30df-414a-a7d5-6a879f40d79e",
"name": "Todo",
"type": "unstarted",
"color": "#e2e2e2"
},
"team": {
"id": "8a193230-9912-4df9-ac1c-b712c36e9ca8",
"key": "BLO",
"name": "BlocksOrg"
},
"creator": {
"id": "95e51d16-13d4-481e-bac9-9b0d6ef528ee",
"name": "dev@blocks.team",
"email": "dev@blocks.team"
},
"labels": [],
"url": "https://linear.app/blocksorg/issue/BLO-14/remove-secrets-from-logs",
"identifier": "BLO-14",
"createdAt": "2025-06-04T03:17:18.674Z",
"updatedAt": "2025-06-04T17:52:00.525Z"
},
"organization": {
"id": "695d139f-6da4-46e0-a4ad-e6e5ba163b5a"
},
}
```
```python agent.py theme={null}
def agent(input):
action = input.get("action")
issue = input.get("issue")
organization = input.get("organization")
```
## Fields
The action that occurred on the issue.
The issue was opened.
The issue was edited. For example, the title or description was changed.
The issue that the event is on.
The id of the issue.
The number of the issue.
The title of the issue.
The description of the issue.
The priority of the issue.
The label for the priority.
The issue state.
The id of the state.
The name of the state.
The type of the state.
The color of the state.
The issue team.
The id of the team.
The key of the team.
The name of the team.
The issue creator.
The id of the creator.
The name of the creator.
The email of the creator.
The labels attached to the issue.
The url of the issue.
The identifier of the issue.
The date/time the issue was created.
The date/time the issue was updated.
The orgnaization associated with the Linear project.
The Linear organization id.
# schedule.daily
Source: https://docs.blocks.team/events/schedule-daily
This event triggers an agent once per day at 1:00 PM UTC. The specific time is not currently configurable.
If you need to configure a specific time, please reach out to us at [support@blocks.team](mailto:support@blocks.team).
```python theme={null}
@on("schedule.daily")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"triggered_at": "2024-01-01T00:00:00Z"
}
```
```python agent.py theme={null}
def agent(input):
triggered_at = input.get("triggered_at")
```
## Fields
The date and time the agent was triggered.
# schedule.weekly
Source: https://docs.blocks.team/events/schedule-weekly
This event triggers an agent once per Monday at 1:00 PM UTC. The specific day and time is not currently configurable.
If you need to configure a specific day and time, please reach out to us at [support@blocks.team](mailto:support@blocks.team).
```python theme={null}
@on("schedule.weekly")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"triggered_at": "2024-01-01T00:00:00Z"
}
```
```python agent.py theme={null}
def agent(input):
triggered_at = input.get("triggered_at")
```
## Fields
The date and time the agent was triggered.
# slack.mention
Source: https://docs.blocks.team/events/slack-mention
```python theme={null}
@on("slack.mention")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"token": "U8smw6EdyKRARkdHfKVt3qdP",
"team_id": "T086XLXR6VB",
"api_app_id": "A08CF7F7SJ1",
"event": {
"user": "U087PAPSV2L",
"type": "app_mention",
"ts": "1749020713.534759",
"text": "<@U08D6SFHEV6> /act-dev create an issue on github for orchestrator, and/or client, but there's an issue where if I don't have an workspace integration and I install an agent, say I update it, if I then install the workspace integration (say slack) I can't enable it until I update it and then enable in",
"channel": "C08PDQ8NYAU"
},
"type": "event_callback",
"$blocks.config.values": {
"additional_instructions_prompt": ""
}
}
```
```python agent.py theme={null}
def agent(input):
token = input.get("token")
team_id = input.get("team_id")
api_app_id = input.get("api_app_id")
event = input.get("event")
slack_type = input.get("type")
blocks_config_values = input.get("$blocks.config.values")
```
## Fields
The Slack webhook token
The Slack team id
The Slack app id
The Slack event object
The user id that invoked the event.
The event type.
The timestamp of the event.
The text content of the message.
The Slack channel id.
The Slack type.
Internal Blocks config values.
The prompt for additional instructions.
# webhook
Source: https://docs.blocks.team/events/webhook
The `webhook` event is a generic event which provisions a unique URL for your agent. You must add the `webhook` event to invoke your agent from the [dashboard](https://blocks.team/app/home)
```python theme={null}
@on("webhook")
def agent(input):
pass
```
The following is an example payload which is passed in as an argument to your agent's entrypoint:
```json json theme={null}
{
"text": "Hello world!"
}
```
```python agent.py theme={null}
def agent(input):
text = input.get("text")
```
## Fields
The message to the agent.
# Release Notes
Source: https://docs.blocks.team/release-notes
Latest updates and changes to Blocks
## v1.2.16
#### Sessions
* **Saved Session Filters** - Session filter preferences now load from workspace-user metadata, can be saved or reset from the filter menu, and default to showing all creators unless you save or explicitly select **Created by Me**
* **Main Panel Session Grouping** - The main panel now remembers your session grouping preference per workspace user
* **Sessions Table Filter Preferences** - The full sessions page now uses the same saved workspace-user filter preferences and supports saving filters directly from its filter dropdown
#### Pull Requests
* **Repository-Grouped Pull Requests** - Session properties now groups associated pull requests by repository, parses GitHub and Bitbucket URLs into clearer labels, and sorts pull requests by creation time
## v1.2.15
#### REST API
* **REST API Release** - Blocks now includes a REST API for creating sessions, polling replies, and sending follow-ups from your own apps. Start with the [REST API Quick Start](/rest-api/quick-start), then create a session with a request like:
```bash theme={null}
curl -X POST https://api.blocks.team/rest/v1/sessions \
-H "Authorization: ApiKey $BLOCKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"agent_name":"claude","message":"Summarize the latest PR activity."}'
```
#### Sessions & UI
* **Improved Linear Session Naming** - Linear-linked sessions now get clearer, more consistent names
* **Linear Session Grouping** - Linear sessions now form session groups automatically when they share the same resource
* **Creator Avatars** - Sessions now show creator avatars so it is easier to see ownership at a glance
* **Automation Run Visibility** - Automation runs are now shown directly on their respective automation pages in Dashboard > Settings
* **Auto-Archive Dialog Caching** - Improved caching for the auto-archive dialog so dismissing it prevents it from reappearing for 30 minutes
#### Pull Requests & Integrations
* **Bitbucket PR Diff Fetching** - Fixed an issue preventing Bitbucket pull request diffs from fetching
* **Duplicate Bitbucket PR Links** - Fixed an issue where Bitbucket sessions could sometimes render duplicate pull request links
#### Agents & Models
* **Codex 5.5 Support** - Codex 5.5 is now available with both Codex and OpenCode agents
* **Kimi 2.6 in OpenCode** - Kimi 2.6 is now available on OpenCode
#### Stability & Reliability
* **Improved Agent Dependency Isolation** - Reduced hanging-session edge cases by improving dependency isolation between agents
## v1.2.14
#### Optimizations
* **Faster Chat Starts** - Optimized chat startup speed for a snappier session launch experience
* **Edge-Routed Requests** - Improved global performance by routing requests through edge locations
* **REST API Rate Limits** - Updated REST API rate limits to `100` requests per minute
#### Agents
* **Sisyphus Plugin Version Pinning** - Fixed issues with Sisyphus by pinning plugin versions more reliably
* **Reliable PR Registration** - Improved pull request registration stability for long-running sessions
* **Kimi 2.6 Compatibility** - Fixed Kimi Code issues when using Kimi 2.6
* **Kimi Code Error Handling** - Improved error handling for Kimi Code
* **Kimi Code Restart Fixes** - Fixed agent restart issues in Kimi Code
* **Claude Code 1M Context Support** - Added support for Claude Code models with a 1 million token context window
* **Opus 4.7 on Bedrock** - Fixed Opus 4.7 support for Claude Code on Bedrock
* **Sub-Agent Visibility** - Claude Code now surfaces more SubAgent activity
## v1.2.13
#### Custom MCP Servers
* **Improved MCP Settings** - Added rename support, clearer required environment variable actions, and a more polished loading and save flow
#### Sessions & Models
* **Shared Session Filters** - Session filters now stay in sync between the main panel and sessions table
* **Profile-Based Model Sync** - Session model selection now follows the latest profile-based model changes more reliably
#### Pull Requests & UI
* **Improved PR Performance** - Large pull request diffs render more smoothly with better chunking and tab behavior
* **UI Polish** - Improved page titles and Vercel preview link handling
## v1.2.12
#### Sessions & Navigation
* **Improved Navigation Performance** - Page loads across the app are now faster
#### Pull Requests & UI
* **Improved PR Panel** - Added inline diff rendering with better handling for large files and multi-PR sessions
* **UI Polish** - Fixed mobile layout issues and improved markdown wrapping in chat
## v1.2.11
#### Platform & Workspace
* **Updated Atlassian OAuth Scopes** - Refined Atlassian OAuth permissions to support the latest integration requirements
* **Workspace-Level Git Configuration** - Workspace admins can now configure a fallback git commit email for unmapped sessions or users without a personal git email configured
* **Disable Private Sessions** - Added a workspace admin setting that disables private sessions
* **Hide the Game** - Added a workspace user setting to hide the game
#### Sessions & Integrations
* **Rename Sessions** - Sessions can now be renamed
* **Enhanced Linear Attachments Support** - Improved handling for Linear attachments
#### Agents & Models
* **Model Selection Across Blocks** - You can now choose the model used for sessions, automations, and PR review runs, and sessions now support switching models mid-session
* **Opus 4.7 Support** - Added support for Opus 4.7
* **Codex 5.4 Support** - Added support for Codex 5.4
* **Gemini Support in OpenCode** - OpenCode now supports Gemini
* **Kimi 2.6 in Kimi Code** - Added support for Kimi 2.6 in Kimi Code
* **Safe Image Read Tool for Claude Code** - Added a safe image read tool for Claude Code that prevalidates images and rejects problematic reads that could otherwise corrupt a session due to an open Claude Code issue
#### Stability & Reliability
* **Claude Code Refresh Tokens** - Improved refresh token stability with Claude Code
* **Gemini CLI Stability** - Improved Gemini CLI agent stability
* **OpenCode Error Handling** - Improved error handling for OpenCode
## v1.2.10
#### PR & Integrations
* **PR Panel** - View file diffs, commits, checks, comments, statuses, and Vercel previews without leaving Blocks
* **GitLab Merge Request Support** - Enables GitLab PR review and automations
* **Installer Actor Mentions (GitLab/Bitbucket)** - The user who authorizes the Blocks integration can now be directly mentioned instead of `@blocks`; as mentioned previously, creating a dedicated integration user for GitLab and Bitbucket is still recommended
* **Workspace-Specific Linear Accounts** - Connected Accounts now lets you link a different Linear account for each workspace
* **Clearer Integration Selection** - Updated guidance on when user integrations are used across the Anthropic/OpenAI integration pages and Claude Code/Codex agent pages
## v1.2.9
#### Docs
* **Attachment Support** - You can now upload attachments directly to Blocks sessions
* **OpenCode Onboarding (Kimi Key)** - Support for `KIMI_API_KEY` with OpenCode during onboarding
* **Installation Profiles** - Create a model/MCP configuration with a custom slash command (for example, `@blocks /claude-fast` mapped to Claude Code with Haiku)
## v1.2.8
#### Custom MCP Servers
* **JSON Configuration** - Define MCP servers directly in the Blocks dashboard using a JSON object specifying the command, arguments, and environment variables
* **Environment Variable Injection** - Reference global environment variables in your MCP config using `${env:VARIABLE_NAME}` — Blocks resolves these at runtime so secrets are never hardcoded
* **Workspace-Wide** - MCP servers are available across your entire workspace and can be enabled or disabled per agent
* **Variable Helper** - A dropdown in the MCP server editor lists your global environment variables and copies the correct template string for you
#### Bug Fixes
* Improved user integration usage to stabilize Anthropic and OpenAI user integrations
## v1.2.7
#### Git Configuration
* **Commit Author Email** - Set a custom email address to use as the commit author across your Blocks sessions from **Dashboard → Settings → Git**
* **Per-User Setting** - This is a personal configuration and does not affect other workspace members
* **Vercel Build Previews** - Setting your commit email to an address in your GitHub, GitLab, or Bitbucket organization enables Vercel to create build previews for PRs opened by Blocks
#### GitHub Bot Invocations
* **New Setting** - Enable bot user invocations from **Dashboard → Settings → GitHub**, allowing GitHub bot users to mention Blocks and create sessions
* **Disabled by Default** - Bot invocations are off by default and must be explicitly enabled per workspace
#### Bug Fixes
* Fixed an issue where Linear agent sessions were not being mapped to the correct workspace user
## v1.2.6
#### Automations
* **Event-Driven Agent Runs** - Trigger coding agents automatically on GitHub PRs, GitLab MRs, Bitbucket PRs and pushes, GitHub Actions failures, and scheduled intervals — no manual mention required
* **Custom Prompts** - Write a prompt per automation that the agent receives whenever the trigger fires
* **Repository Scoping** - Scope automations to specific repositories or run across all connected repos
* **CI Check Display** - Show automation results as a native CI check on pull requests, blocking merges until the automation passes
* **Workspace or Private Visibility** - Share automations across your workspace or keep them private
#### PR Review
* **Automatic PR Reviews** - Run a coding agent on every pull request across your connected repositories without any manual mention
* **Per-Repo Control** - Enable or disable reviews for individual repositories from a single settings page
* **Custom Review Instructions** - Add a `.blocks/review.md` file to a repository to customize what the agent looks for in that repo's PRs
* **Agent Selection** - Choose any configured agent for reviews
* **Supported Platforms** - Works with GitHub, GitLab, and Bitbucket
#### Global Configuration
* **Global Configuration Repository** - Set any connected repository as your workspace's global configuration repo from **Settings → Global Configuration**
* **Automatic Loading** - Every Blocks session will automatically load Skills, Hooks, and Sub-Agents from the configured repository at runtime
## v1.2.5
#### Session Groups
* **Linked Sessions** - Sessions that share a common resource (such as a Linear issue, GitHub PR, or Slack thread) are now automatically grouped together
* **View Related Sessions** - Open the session properties panel on any session page to see other sessions in the same group
* **Filter by Resource** - Adjust the sessions list filters to group by resource, giving you a consolidated view of all activity around a specific issue, PR, or thread
## v1.2.4
#### Organization Workspaces
* **Self-Serve Creation** - Create an organization workspace anytime from the workspace switcher in the bottom-left of the dashboard — no onboarding required
* **Role-Based Access** - Invite team members as Admins or Members, with admins controlling what members can see and do
* **Admin Controls** - Restrict member visibility into API keys, environment variables, integrations, analytics, and more from **Settings → Permissions**
* **Workspace Isolation** - Repositories, skills, sessions, and settings are fully isolated between workspaces
#### UI Revamp
* **Refreshed Dashboard** - Updated visual design across the dashboard for a cleaner, more consistent experience
* **Workspace Switcher** - New workspace switcher in the bottom-left sidebar for faster context switching
## v1.2.3
#### Kimi Code Agent
* **Moonshot AI Integration** - Kimi Code is now available in Blocks, powered by Moonshot AI's Kimi models
* **API Key Configuration** - Connect your Kimi API key from the Blocks dashboard under **Agents → Kimi Code**
* **Invoke via Slash Command** - Use `/kimi` to run Kimi Code on any request, or set it as your default agent
* **Experimental Status** - Available now as an experimental agent
## v1.2.2
#### Cursor CLI Agent
* **Premium Models** - Access Composer, GPT-5.2-codex, Claude 4.5 (Sonnet/Opus), and Gemini 3 Pro through a single subscription
* **Subscription Model** - Requires Cursor Pro or Team subscription with API key
* **Invoke via Slash Command** - Use `/cursor` to invoke (cannot be set as default agent)
#### Support for PR Skills & Hooks
* **Agent PR Creation Skills** - Define custom agent skills for pull request creation with personalized templates and workflows
* **PR Hooks Support** - Configure hooks to customize pull request templates and defaults
* **Draft Mode Control** - Set default draft mode and other PR settings through custom skills or hooks
```markdown .claude/skills/SKILL.md theme={null}
---
name: create-pr
description: Generate and open a pull request using the repository PR template and GitHub CLI. Use when asked to create a PR, open a pull request, or submit changes for review.
---
# Open Pull Request
Generate a pull request that conforms to organization standards.
...
```
## v1.2.1
#### Sisyphus Agent
* **Multi-Agent Orchestration** - Introducing Sisyphus (Oh My OpenCode), a sophisticated orchestration system that coordinates multiple specialized agents
* **Specialized Agent Team** - Main Conductor (Claude Opus 4.5), Oracle (GPT-5), and Backend Specialist (Claude Sonnet 4.5) work together on complex tasks
* **Complex Workflows** - Ideal for full-stack feature implementation, large-scale refactoring, and multi-file changes requiring domain expertise
* **Claude Code Compatibility** - Built with compatibility layer for seamless integration
* **API Key Configuration** - Supports both Anthropic and OpenAI API keys for full capabilities
* **Experimental Status** - Available now as an experimental agent via `/sisyphus` slash command
## v1.2.0
#### OpenCode
* You can now use OpenCode in Blocks!
* **OpenAI and Anthropic API Key Support** - Configure OpenCode with your OpenAI or Anthropic API keys
#### Database Integration
* **Postgres Support** - Connect a Postgres database to your Blocks workspace
* **Bastion Connection** - Secure database connections through SSH bastion hosts with support for private keys and credentials
#### Linear Integration
* **Default Assign Mode** - Configure the default mode (Edit or Plan) when assigning Linear tickets to agents, streamlining your workflow
#### Infrastructure
* **GitHub Token Refresh** - Improved GitHub token refresh mechanism in agent sandboxes for more reliable authentication
## v1.1.0
#### Plan Mode
Now you can create a plan before implementation happens:
* **Mode Switching** - Toggle between Plan and Edit mode when creating a new session from the dashboard, Slack, GitHub, and Linear
* **Plan Visualization** - See your implementation plan displayed clearly in the session
* **Make Changes** - Work with the agent to update the plan
* **One-Click Implementation** - Implement planned changes with a single button
* **Use Anywhere** - Run plan mode from the Dashboard, GitHub, Slack, and Linear
#### Post-Clone Scripts
* Add a `.blocks/post-clone` or `.blocks/post-clone.sh` with one or more shell commands directly in your repository:
```bash theme={null}
#!/bin/bash
npm run i
```
#### Claude Code Hook and Skills Support
* Read directly from `.claude` folders in cloned repositories
#### Enhanced File Viewer
* **Inline File Viewing** - View files directly in chat conversations
* **Full-Screen Mode** - Dedicated full-screen viewer for focused file review
#### Enhanced TODOs
* **Collapsible TODO List** - View all in-progress TODOs directly above your message box in a session
* **Reduce Clutter** - TODO updates no longer clutter your main chat history
#### AWS Integration
* **Credential Configuration** - Set up AWS access keys and secrets from the settings page
* **OIDC Support Coming Soon**
* **Bedrock API Keys** - Claude Code can now be configured to use an AWS Bedrock API key
#### Linear Integration
* **Default Team Configuration** - Set a default Linear team for new issue creation, overwrite by specifying a specific team
#### Other Improvements
* **Session Details** - View creation dates for all your sessions in the sidebar
* **Unarchive Sessions** - Restore archived sessions when needed
* **GitHub Branches** - Quick access to branches from the GitHub menu in message box command menus
* **Preserved Redirects** - Return to your intended page after signing in when visiting an unauthenticated link
* **Improved User Management** - Filtered non-human users from the Linear claim user dialog
# Quick Start
Source: https://docs.blocks.team/rest-api/quick-start
Create an agent session, stream the first response, and send a follow-up.
The Sessions API lets you start a conversation with a Blocks agent and stream its responses over plain HTTP. Every endpoint lives under `/rest/v1`, accepts and returns JSON, and authenticates with a workspace-scoped API key.
| | |
| --------------- | -------------------------------------- |
| **Base URL** | `https://api.blocks.team` |
| **Auth header** | `Authorization: ApiKey ` |
| **Rate limit** | 100 requests / minute / API key |
## 1. Create a Blocks account and get an API key
Create a [Blocks account](https://blocks.team/signup), then generate a workspace API key from **Settings** > **API Keys**. Set the API key in your environment as `BLOCKS_API_KEY` before running the snippets below. For example:
```bash theme={null}
export BLOCKS_API_KEY="your_api_key_here"
```
## 2. Create a session and poll for the first reply
1. **Create a session** — `POST /rest/v1/sessions`. The response includes `_links.final_message.href`, a pre-built URL for polling the assistant's reply.
2. **Poll the URL** — `GET` it until `items` is non-empty.
```javascript JavaScript theme={null}
const BASE_URL = "https://api.blocks.team";
const headers = {
Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}`,
"Content-Type": "application/json",
};
// 1. Create a session.
const session = await fetch(`${BASE_URL}/rest/v1/sessions`, {
method: "POST",
headers,
body: JSON.stringify({
agent_name: "claude",
message: "Say hello and tell me a one-sentence fun fact about octopuses.",
}),
}).then((r) => r.json());
// 2. Poll for the assistant's final message.
while (true) {
const page = await fetch(session._links.final_message.href, { headers }).then((r) => r.json());
if (page.items.length > 0) {
console.log(page.items[0].message);
break;
}
await new Promise((r) => setTimeout(r, 5000));
}
```
```python Python theme={null}
import os, time, requests
BASE_URL = "https://api.blocks.team"
HEADERS = {
"Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}",
"Content-Type": "application/json",
}
# 1. Create a session.
session = requests.post(f"{BASE_URL}/rest/v1/sessions", headers=HEADERS, json={
"agent_name": "claude",
"message": "Say hello and tell me a one-sentence fun fact about octopuses.",
}).json()
# 2. Poll for the assistant's final message.
while True:
page = requests.get(session["_links"]["final_message"]["href"], headers=HEADERS).json()
if page["items"]:
print(page["items"][0]["message"])
break
time.sleep(5)
```
```bash cURL theme={null}
BASE_URL="https://api.blocks.team"
AUTH="Authorization: ApiKey $BLOCKS_API_KEY"
# 1. Create a session.
SESSION=$(curl -s -X POST "$BASE_URL/rest/v1/sessions" \
-H "$AUTH" -H "Content-Type: application/json" \
-d '{"agent_name":"claude","message":"Say hello and tell me a one-sentence fun fact about octopuses."}')
SESSION_ID=$(echo "$SESSION" | jq -r '.id')
FINAL_URL=$(echo "$SESSION" | jq -r '._links.final_message.href')
# 2. Poll for the assistant's final message.
while :; do
PAGE=$(curl -s -H "$AUTH" "$FINAL_URL")
if [ "$(echo "$PAGE" | jq '.items | length')" -gt 0 ]; then
echo "$PAGE" | jq -r '.items[0].message'
break
fi
sleep 5
done
```
```java Java theme={null}
import java.net.URI;
import java.net.http.*;
import com.fasterxml.jackson.databind.*;
String BASE_URL = "https://api.blocks.team";
String AUTH = "ApiKey " + System.getenv("BLOCKS_API_KEY");
HttpClient http = HttpClient.newHttpClient();
ObjectMapper json = new ObjectMapper();
// 1. Create a session.
JsonNode session = json.readTree(http.send(
HttpRequest.newBuilder(URI.create(BASE_URL + "/rest/v1/sessions"))
.header("Authorization", AUTH).header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
"{\"agent_name\":\"claude\",\"message\":\"Say hello and tell me a one-sentence fun fact about octopuses.\"}"))
.build(),
HttpResponse.BodyHandlers.ofString()).body());
String sessionId = session.get("id").asText();
String finalUrl = session.at("/_links/final_message/href").asText();
// 2. Poll for the assistant's final message.
while (true) {
JsonNode page = json.readTree(http.send(
HttpRequest.newBuilder(URI.create(finalUrl)).header("Authorization", AUTH).build(),
HttpResponse.BodyHandlers.ofString()).body());
if (page.get("items").size() > 0) {
System.out.println(page.at("/items/0/message").asText());
break;
}
Thread.sleep(5000);
}
```
```ruby Ruby theme={null}
require "net/http"
require "json"
BASE_URL = "https://api.blocks.team"
HEADERS = {
"Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}",
"Content-Type" => "application/json",
}
# 1. Create a session.
session = JSON.parse(Net::HTTP.post(
URI("#{BASE_URL}/rest/v1/sessions"),
JSON.generate({
agent_name: "claude",
message: "Say hello and tell me a one-sentence fun fact about octopuses.",
}),
HEADERS,
).body)
# 2. Poll for the assistant's final message.
loop do
page = JSON.parse(Net::HTTP.get(URI(session["_links"]["final_message"]["href"]), HEADERS))
break puts(page["items"][0]["message"]) if page["items"].any?
sleep 5
end
```
```go Go theme={null}
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
const BaseURL = "https://api.blocks.team"
func main() {
auth := "ApiKey " + os.Getenv("BLOCKS_API_KEY")
// 1. Create a session.
body, _ := json.Marshal(map[string]string{
"agent_name": "claude",
"message": "Say hello and tell me a one-sentence fun fact about octopuses.",
})
req, _ := http.NewRequest("POST", BaseURL+"/rest/v1/sessions", bytes.NewReader(body))
req.Header.Set("Authorization", auth)
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
var session struct {
ID string `json:"id"`
Links struct {
FinalMessage struct{ Href string } `json:"final_message"`
} `json:"_links"`
}
json.NewDecoder(res.Body).Decode(&session)
res.Body.Close()
// 2. Poll for the assistant's final message.
for {
req, _ := http.NewRequest("GET", session.Links.FinalMessage.Href, nil)
req.Header.Set("Authorization", auth)
res, _ := http.DefaultClient.Do(req)
var page struct {
Items []struct{ Message string } `json:"items"`
}
json.NewDecoder(res.Body).Decode(&page)
res.Body.Close()
if len(page.Items) > 0 {
fmt.Println(page.Items[0].Message)
break
}
time.Sleep(5 * time.Second)
}
}
```
## 3. Send a follow-up
3. **Send a follow-up** — `POST /rest/v1/sessions/{session_id}/messages`. The response returns its own `_links.final_message.href`, the same shortcut as create.
4. **Poll the new thread's URL** — same pattern as above.
Follow-ups can be sent at any time — including while the agent is still working — and will interrupt the in-flight turn.
The snippets below assume `session`, `headers`, and `BASE_URL` are still in scope from the previous step.
```javascript JavaScript theme={null}
// 3. Send a follow-up.
const followup = await fetch(`${BASE_URL}/rest/v1/sessions/${session.id}/messages`, {
method: "POST",
headers,
body: JSON.stringify({ message: "Cool — now tell me one about cuttlefish." }),
}).then((r) => r.json());
// 4. Poll the follow-up's thread.
while (true) {
const page = await fetch(followup._links.final_message.href, { headers }).then((r) => r.json());
if (page.items.length > 0) {
console.log(page.items[0].message);
break;
}
await new Promise((r) => setTimeout(r, 5000));
}
```
```python Python theme={null}
# 3. Send a follow-up.
followup = requests.post(
f"{BASE_URL}/rest/v1/sessions/{session['id']}/messages",
headers=HEADERS,
json={"message": "Cool — now tell me one about cuttlefish."},
).json()
# 4. Poll the follow-up's thread.
while True:
page = requests.get(followup["_links"]["final_message"]["href"], headers=HEADERS).json()
if page["items"]:
print(page["items"][0]["message"])
break
time.sleep(5)
```
```bash cURL theme={null}
# 3. Send a follow-up.
FOLLOWUP=$(curl -s -X POST "$BASE_URL/rest/v1/sessions/$SESSION_ID/messages" \
-H "$AUTH" -H "Content-Type: application/json" \
-d '{"message":"Cool — now tell me one about cuttlefish."}')
FOLLOW_URL=$(echo "$FOLLOWUP" | jq -r '._links.final_message.href')
# 4. Poll the follow-up's thread.
while :; do
PAGE=$(curl -s -H "$AUTH" "$FOLLOW_URL")
if [ "$(echo "$PAGE" | jq '.items | length')" -gt 0 ]; then
echo "$PAGE" | jq -r '.items[0].message'
break
fi
sleep 5
done
```
```java Java theme={null}
// 3. Send a follow-up.
JsonNode followup = json.readTree(http.send(
HttpRequest.newBuilder(URI.create(BASE_URL + "/rest/v1/sessions/" + sessionId + "/messages"))
.header("Authorization", AUTH).header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
"{\"message\":\"Cool — now tell me one about cuttlefish.\"}"))
.build(),
HttpResponse.BodyHandlers.ofString()).body());
String followUrl = followup.at("/_links/final_message/href").asText();
// 4. Poll the follow-up's thread.
while (true) {
JsonNode page = json.readTree(http.send(
HttpRequest.newBuilder(URI.create(followUrl)).header("Authorization", AUTH).build(),
HttpResponse.BodyHandlers.ofString()).body());
if (page.get("items").size() > 0) {
System.out.println(page.at("/items/0/message").asText());
break;
}
Thread.sleep(5000);
}
```
```ruby Ruby theme={null}
# 3. Send a follow-up.
followup = JSON.parse(Net::HTTP.post(
URI("#{BASE_URL}/rest/v1/sessions/#{session['id']}/messages"),
JSON.generate({ message: "Cool — now tell me one about cuttlefish." }),
HEADERS,
).body)
# 4. Poll the follow-up's thread.
loop do
page = JSON.parse(Net::HTTP.get(URI(followup["_links"]["final_message"]["href"]), HEADERS))
break puts(page["items"][0]["message"]) if page["items"].any?
sleep 5
end
```
```go Go theme={null}
// 3. Send a follow-up.
body, _ := json.Marshal(map[string]string{
"message": "Cool — now tell me one about cuttlefish.",
})
req, _ := http.NewRequest("POST", BaseURL+"/rest/v1/sessions/"+session.ID+"/messages", bytes.NewReader(body))
req.Header.Set("Authorization", auth)
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
var followup struct {
Links struct {
FinalMessage struct{ Href string } `json:"final_message"`
} `json:"_links"`
}
json.NewDecoder(res.Body).Decode(&followup)
res.Body.Close()
// 4. Poll the follow-up's thread.
for {
req, _ := http.NewRequest("GET", followup.Links.FinalMessage.Href, nil)
req.Header.Set("Authorization", auth)
res, _ := http.DefaultClient.Do(req)
var page struct {
Items []struct{ Message string } `json:"items"`
}
json.NewDecoder(res.Body).Decode(&page)
res.Body.Close()
if len(page.Items) > 0 {
fmt.Println(page.Items[0].Message)
break
}
time.Sleep(5 * time.Second)
}
```
## Next steps
Full request and response schema for `POST /rest/v1/sessions`.
List, filter, and poll messages on a session or a single thread.
Post follow-ups — they interrupt an in-flight session.
Look up a single session by ID.
# Create Session
Source: https://docs.blocks.team/rest-api/sessions/create
Start a new agent session and post the first user message.
```http theme={null}
POST /rest/v1/sessions
```
Creates a new session for the authenticated workspace, posts the initial user message, and dispatches it to the agent. The response includes a pre-built `_links.final_message.href` URL — poll it to receive the assistant's reply.
## Request
```javascript JavaScript theme={null}
const res = await fetch("https://api.blocks.team/rest/v1/sessions", {
method: "POST",
headers: {
Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
agent_name: "claude",
message: "Say hello and tell me a one-sentence fun fact about octopuses.",
}),
});
const session = await res.json();
```
```python Python theme={null}
import os, requests
session = requests.post(
"https://api.blocks.team/rest/v1/sessions",
headers={
"Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}",
"Content-Type": "application/json",
},
json={
"agent_name": "claude",
"message": "Say hello and tell me a one-sentence fun fact about octopuses.",
},
).json()
```
```bash cURL theme={null}
curl -X POST https://api.blocks.team/rest/v1/sessions \
-H "Authorization: ApiKey $BLOCKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_name": "claude",
"message": "Say hello and tell me a one-sentence fun fact about octopuses."
}'
```
```java Java theme={null}
import java.net.URI;
import java.net.http.*;
HttpResponse res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create("https://api.blocks.team/rest/v1/sessions"))
.header("Authorization", "ApiKey " + System.getenv("BLOCKS_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
"{\"agent_name\":\"claude\",\"message\":\"Say hello and tell me a one-sentence fun fact about octopuses.\"}"))
.build(),
HttpResponse.BodyHandlers.ofString());
```
```ruby Ruby theme={null}
require "net/http"
require "json"
session = JSON.parse(Net::HTTP.post(
URI("https://api.blocks.team/rest/v1/sessions"),
JSON.generate({
agent_name: "claude",
message: "Say hello and tell me a one-sentence fun fact about octopuses.",
}),
{
"Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}",
"Content-Type" => "application/json",
},
).body)
```
```go Go theme={null}
body, _ := json.Marshal(map[string]string{
"agent_name": "claude",
"message": "Say hello and tell me a one-sentence fun fact about octopuses.",
})
req, _ := http.NewRequest("POST", "https://api.blocks.team/rest/v1/sessions", bytes.NewReader(body))
req.Header.Set("Authorization", "ApiKey "+os.Getenv("BLOCKS_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
```
### Body
Exactly one of `agent_id`, `agent_name`, or `profile` must be provided. Sending none returns `400 VALIDATION`.
The first user message in the session.
Name of a built-in core agent. One of `claude`, `codex`, `gemini`, `opencode`, `cursor`, `kimi`. Returns `400 VALIDATION` if the name is unknown.
ID of a specific agent in your workspace. Use this to target a custom agent.
Reference to a profile or an inline profile to apply to this session.
ID of an existing profile.
Runtime config overrides for the agent.
MCP servers to attach to the session.
How `config_values` are merged with the agent's defaults.
When `true`, the session is hidden from other workspace members.
Existing artifacts to attach to the initial message.
## Response
```json theme={null}
{
"id": "a196cec6-49cb-4c48-8e4c-2707fb5d6709",
"title": "Octopus fun fact",
"pull_requests": [],
"source_url": null,
"is_archived": false,
"is_private": false,
"created_at": "2026-04-30T18:21:00.000Z",
"updated_at": "2026-04-30T18:21:00.000Z",
"thread_id": "71562cd0-1f63-41b2-8382-10f2fdb1ac8b",
"session_html_url": "https://blocks.team/app/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709",
"_links": {
"self": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709" },
"messages": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709/messages" },
"thread": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709/threads/71562cd0-1f63-41b2-8382-10f2fdb1ac8b/messages" },
"final_message": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709/threads/71562cd0-1f63-41b2-8382-10f2fdb1ac8b/messages?type=final_message&role=assistant" }
}
}
```
Unique identifier for the session.
Auto-generated title for the session.
IDs of pull requests opened by the agent in this session.
External URL the session was started from, if any (e.g. a Slack message).
Whether the session has been archived.
Whether the session is hidden from other workspace members.
When the session was created.
When the session was last updated.
The ID of the first thread on the session. Used to build the polling URL for the assistant's reply.
Web URL where the session can be viewed in the Blocks dashboard.
HATEOAS links for the session.
Link to this session.
Link to the session's messages list.
Link to the first thread's messages.
Pre-built polling URL for the assistant's final message — already filtered to `type=final_message&role=assistant` on the first thread.
## Errors
| Status | Code | Reason |
| ------ | ------------ | ------------------------------------------------------------ |
| `400` | `VALIDATION` | None of `agent_id`, `agent_name`, or `profile` was provided. |
| `400` | `VALIDATION` | `agent_name` is not one of the supported core agents. |
# Send Messages
Source: https://docs.blocks.team/rest-api/sessions/follow-up
Post a follow-up user message to an existing session.
```http theme={null}
POST /rest/v1/sessions/{session_id}/messages
```
Posts a follow-up user message to an existing session and starts a new agent turn. The response returns a fresh `chat_thread_id` — use it to build the polling URL for the assistant's reply.
Follow-ups can be sent at any time, including while the agent is still working on a previous turn. They will **interrupt** the in-flight turn.
## Request
```javascript JavaScript theme={null}
const followup = await fetch(
`https://api.blocks.team/rest/v1/sessions/${sessionId}/messages`,
{
method: "POST",
headers: {
Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: "Cool — now tell me one about cuttlefish." }),
},
).then((r) => r.json());
// Poll followup._links.final_message.href to receive the assistant's reply.
```
```python Python theme={null}
import os, requests
followup = requests.post(
f"https://api.blocks.team/rest/v1/sessions/{session_id}/messages",
headers={
"Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}",
"Content-Type": "application/json",
},
json={"message": "Cool — now tell me one about cuttlefish."},
).json()
# Poll followup["_links"]["final_message"]["href"] to receive the assistant's reply.
```
```bash cURL theme={null}
curl -X POST "https://api.blocks.team/rest/v1/sessions/$SESSION_ID/messages" \
-H "Authorization: ApiKey $BLOCKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"message": "Cool — now tell me one about cuttlefish."}'
```
```java Java theme={null}
import java.net.URI;
import java.net.http.*;
HttpResponse res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create(
"https://api.blocks.team/rest/v1/sessions/" + sessionId + "/messages"))
.header("Authorization", "ApiKey " + System.getenv("BLOCKS_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
"{\"message\":\"Cool — now tell me one about cuttlefish.\"}"))
.build(),
HttpResponse.BodyHandlers.ofString());
```
```ruby Ruby theme={null}
require "net/http"
require "json"
followup = JSON.parse(Net::HTTP.post(
URI("https://api.blocks.team/rest/v1/sessions/#{session_id}/messages"),
JSON.generate({ message: "Cool — now tell me one about cuttlefish." }),
{
"Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}",
"Content-Type" => "application/json",
},
).body)
```
```go Go theme={null}
body, _ := json.Marshal(map[string]string{
"message": "Cool — now tell me one about cuttlefish.",
})
req, _ := http.NewRequest("POST",
"https://api.blocks.team/rest/v1/sessions/"+sessionID+"/messages",
bytes.NewReader(body))
req.Header.Set("Authorization", "ApiKey "+os.Getenv("BLOCKS_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
```
### Path parameters
The session to post the follow-up to.
### Body
The follow-up user message.
Existing artifacts to attach to this message.
Profile reference or inline profile to apply to this turn. Same shape as in [`Create Session`](/rest-api/sessions/create#body).
## Response
```json theme={null}
{
"id": "9c2f…",
"chat_id": "a196cec6-49cb-4c48-8e4c-2707fb5d6709",
"chat_thread_id": "b4e1…",
"task_id": "1f8a…",
"role": "user",
"type": "message",
"message": "Cool — now tell me one about cuttlefish.",
"ts": null,
"created_at": "2026-04-30T18:25:12.000Z",
"updated_at": "2026-04-30T18:25:12.000Z",
"_links": {
"self": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/messages" },
"thread": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/threads/b4e1…/messages" },
"final_message": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/threads/b4e1…/messages?type=final_message&role=assistant" }
}
}
```
Message ID for the user message you just posted.
The session this message belongs to.
The new thread created for this turn. Prefer `_links.final_message.href` for polling — this field is exposed for cases where you need the thread ID directly.
The task ID for the agent invocation triggered by this message.
Always `user` for this endpoint.
Always `message` for this endpoint.
Echo of the message body you posted.
Provider-supplied timestamp (epoch seconds). Typically `null` for user messages.
When the message was created.
When the message was last updated.
HATEOAS links — mirror those returned by [`Create Session`](/rest-api/sessions/create) so you can reuse the same polling idiom for follow-ups.
Link to the session's messages collection.
Link to the new thread's messages list.
Pre-built polling URL for the assistant's reply on this thread — already filtered to `type=final_message&role=assistant`. Poll until `items` is non-empty.
## Errors
| Status | Code | Reason |
| ------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | `BAD_REQUEST` | The session has no prior messages, so no `task_id` can be resolved. Create the session via [`Create Session`](/rest-api/sessions/create) first. |
| `404` | `NOT_FOUND` | Session does not exist or belongs to a different workspace. |
# Get Session
Source: https://docs.blocks.team/rest-api/sessions/get
Fetch a single session by ID.
```http theme={null}
GET /rest/v1/sessions/{session_id}
```
Returns the session record for the authenticated workspace. Use this to look up a session's metadata after it was created — for example, to resume a conversation by listing its messages.
## Request
```javascript JavaScript theme={null}
const session = await fetch(
`https://api.blocks.team/rest/v1/sessions/${sessionId}`,
{ headers: { Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}` } },
).then((r) => r.json());
```
```python Python theme={null}
import os, requests
session = requests.get(
f"https://api.blocks.team/rest/v1/sessions/{session_id}",
headers={"Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}"},
).json()
```
```bash cURL theme={null}
curl https://api.blocks.team/rest/v1/sessions/$SESSION_ID \
-H "Authorization: ApiKey $BLOCKS_API_KEY"
```
```java Java theme={null}
import java.net.URI;
import java.net.http.*;
HttpResponse res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create("https://api.blocks.team/rest/v1/sessions/" + sessionId))
.header("Authorization", "ApiKey " + System.getenv("BLOCKS_API_KEY"))
.build(),
HttpResponse.BodyHandlers.ofString());
```
```ruby Ruby theme={null}
require "net/http"
require "json"
session = JSON.parse(Net::HTTP.get(
URI("https://api.blocks.team/rest/v1/sessions/#{session_id}"),
{ "Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}" },
))
```
```go Go theme={null}
req, _ := http.NewRequest("GET", "https://api.blocks.team/rest/v1/sessions/"+sessionID, nil)
req.Header.Set("Authorization", "ApiKey "+os.Getenv("BLOCKS_API_KEY"))
res, _ := http.DefaultClient.Do(req)
```
### Path parameters
The ID of the session to fetch.
## Response
Returns the same shape as [`Create Session`](/rest-api/sessions/create#response), with one difference: `thread_id`, `_links.thread`, and `_links.final_message` are always `null` here. Build polling URLs from message responses or from `_links.messages` instead.
```json theme={null}
{
"id": "a196cec6-49cb-4c48-8e4c-2707fb5d6709",
"title": "Octopus fun fact",
"pull_requests": [],
"source_url": null,
"is_archived": false,
"is_private": false,
"created_at": "2026-04-30T18:21:00.000Z",
"updated_at": "2026-04-30T18:21:42.000Z",
"thread_id": null,
"session_html_url": "https://blocks.team/app/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709",
"_links": {
"self": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709" },
"messages": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-49cb-4c48-8e4c-2707fb5d6709/messages" },
"thread": null,
"final_message": null
}
}
```
## Errors
| Status | Code | Reason |
| ------ | ----------- | ----------------------------------------------------------- |
| `404` | `NOT_FOUND` | Session does not exist or belongs to a different workspace. |
# Get Session Messages
Source: https://docs.blocks.team/rest-api/sessions/messages
List, filter, and poll messages on a session or a single thread.
Two endpoints share the same paginated response shape and query parameters. Use the first to list every message in a session; use the second to scope to a single thread (for example, to poll the latest follow-up's reply).
```http theme={null}
GET /rest/v1/sessions/{session_id}/messages
GET /rest/v1/sessions/{session_id}/threads/{thread_id}/messages
```
Results are sorted by `created_at` (newest first by default). Soft-deleted messages are excluded.
## Request
```javascript JavaScript theme={null}
// All messages on the session.
const url = `https://api.blocks.team/rest/v1/sessions/${sessionId}/messages?type=final_message&role=assistant`;
// Or scope to a specific thread:
// const url = `https://api.blocks.team/rest/v1/sessions/${sessionId}/threads/${threadId}/messages?type=final_message&role=assistant`;
const page = await fetch(url, {
headers: { Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}` },
}).then((r) => r.json());
```
```python Python theme={null}
import os, requests
# All messages on the session.
url = f"https://api.blocks.team/rest/v1/sessions/{session_id}/messages"
# Or scope to a specific thread:
# url = f"https://api.blocks.team/rest/v1/sessions/{session_id}/threads/{thread_id}/messages"
page = requests.get(
url,
headers={"Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}"},
params={"type": "final_message", "role": "assistant"},
).json()
```
```bash cURL theme={null}
# All messages on the session.
curl "https://api.blocks.team/rest/v1/sessions/$SESSION_ID/messages?type=final_message&role=assistant" \
-H "Authorization: ApiKey $BLOCKS_API_KEY"
# Or scope to a specific thread:
curl "https://api.blocks.team/rest/v1/sessions/$SESSION_ID/threads/$THREAD_ID/messages?type=final_message&role=assistant" \
-H "Authorization: ApiKey $BLOCKS_API_KEY"
```
```java Java theme={null}
import java.net.URI;
import java.net.http.*;
String url = "https://api.blocks.team/rest/v1/sessions/" + sessionId
+ "/messages?type=final_message&role=assistant";
// Or scope to a specific thread:
// String url = "https://api.blocks.team/rest/v1/sessions/" + sessionId
// + "/threads/" + threadId + "/messages?type=final_message&role=assistant";
HttpResponse res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create(url))
.header("Authorization", "ApiKey " + System.getenv("BLOCKS_API_KEY"))
.build(),
HttpResponse.BodyHandlers.ofString());
```
```ruby Ruby theme={null}
require "net/http"
require "json"
# All messages on the session.
url = "https://api.blocks.team/rest/v1/sessions/#{session_id}/messages?type=final_message&role=assistant"
# Or scope to a specific thread:
# url = "https://api.blocks.team/rest/v1/sessions/#{session_id}/threads/#{thread_id}/messages?type=final_message&role=assistant"
page = JSON.parse(Net::HTTP.get(URI(url), {
"Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}",
}))
```
```go Go theme={null}
url := "https://api.blocks.team/rest/v1/sessions/" + sessionID +
"/messages?type=final_message&role=assistant"
// Or scope to a specific thread:
// url := "https://api.blocks.team/rest/v1/sessions/" + sessionID +
// "/threads/" + threadID + "/messages?type=final_message&role=assistant"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "ApiKey "+os.Getenv("BLOCKS_API_KEY"))
res, _ := http.DefaultClient.Do(req)
```
### Path parameters
The session ID. Required for both URL forms.
The thread ID. Required only on the threaded URL form. The thread must belong to `session_id`.
### Query parameters
Repeat to combine. Allowed values: `message`, `final_message`, `tool_call`. Pass `type=final_message` to fetch only completed assistant replies.
Filter by sender. One of `user`, `assistant`.
Filter to a single thread without using the threaded URL form. Ignored on the threaded URL.
Cursor for incremental polling, in epoch seconds. Returns only messages whose `ts` is strictly greater than this value. Use `_links.new_messages.href` from the previous page to get a pre-built URL with this cursor already set.
1-indexed page number. Minimum `1`.
Page size. Minimum `1`, maximum `100`.
Sort direction. One of `asc`, `desc`.
## Response
```json theme={null}
{
"items": [
{
"id": "5e1b…",
"chat_id": "a196cec6-49cb-4c48-8e4c-2707fb5d6709",
"chat_thread_id": "71562cd0-1f63-41b2-8382-10f2fdb1ac8b",
"task_id": "0e7d…",
"role": "assistant",
"type": "final_message",
"message": "Hi! Octopuses have three hearts — two pump blood to the gills and one to the rest of the body.",
"ts": 1746038518,
"created_at": "2026-04-30T18:21:42.000Z",
"updated_at": "2026-04-30T18:21:42.000Z"
}
],
"meta": { "total": 1, "page": 1, "limit": 50, "total_pages": 1 },
"_links": {
"self": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/messages?type=final_message&role=assistant" },
"new_messages": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/messages?type=final_message&role=assistant>s=1746038518" }
}
}
```
Page of messages.
Message ID.
The session this message belongs to.
The thread the message was sent on, if any.
The task ID for the agent invocation that produced this message.
Sender role: `user` or `assistant`.
Message type: `message`, `final_message`, or `tool_call`.
The message body.
Provider-supplied timestamp in epoch seconds. Use as the `gts` cursor for incremental polling.
When the message was created.
When the message was last updated.
Pagination metadata.
Total messages matching the filter.
The current 1-indexed page.
The page size used for this response.
Total page count for the current filter.
HATEOAS links for the page.
Link to the current page (preserves all query parameters).
Pre-built URL with `?gts=` appended. Poll this URL to receive only messages newer than the latest one in this page. Always present — when `items` is empty, the cursor is the server's current epoch second, so the next poll returns anything created after now.
## Polling tips
* For the assistant's first reply, use the `_links.final_message.href` returned from [`Create Session`](/rest-api/sessions/create) — it already has the right filters applied.
* For incremental polling, use `_links.new_messages.href` from the previous response. It encodes the `gts` cursor so each poll only returns new messages.
## Errors
| Status | Code | Reason |
| ------ | ----------- | ----------------------------------------------------------------------- |
| `404` | `NOT_FOUND` | Session (or thread) does not exist or belongs to a different workspace. |
# Control Existing Sessions
Source: https://docs.blocks.team/rest-api/use-cases/control-existing-sessions
Use a session ID to inspect and continue an existing Blocks session.
If you already have a `session_id`, you can treat that session as a durable API resource. This lets backend services, internal tools, or operators reconnect to work that is already in progress instead of creating a new session each time.
## What you can do
* Fetch session metadata to understand current state and links
* List messages to read the full conversation history
* Send follow-up messages to continue or redirect the session
* Poll `final_message` to wait for the next assistant result
## Common pattern
Use this flow when a session was created earlier by another system, user action, or automation step:
1. Store the `session_id` when the session is created.
2. Load the session later with `GET /rest/v1/sessions/{session_id}`.
3. Read prior messages with `GET /rest/v1/sessions/{session_id}/messages`.
4. Send a new instruction with `POST /rest/v1/sessions/{session_id}/messages`.
5. Poll the returned `_links.final_message.href` to receive the assistant's next completed reply.
## Example
```javascript theme={null}
const headers = {
Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}`,
"Content-Type": "application/json",
};
const sessionId = "a196cec6-49cb-4c48-8e4c-2707fb5d6709";
const session = await fetch(
`https://api.blocks.team/rest/v1/sessions/${sessionId}`,
{ headers },
).then((r) => r.json());
const history = await fetch(session._links.messages.href, { headers }).then((r) => r.json());
const followup = await fetch(
`https://api.blocks.team/rest/v1/sessions/${sessionId}/messages`,
{
method: "POST",
headers,
body: JSON.stringify({
message: "Summarize the current plan and suggest the next best action.",
}),
},
).then((r) => r.json());
const finalMessage = await fetch(followup._links.final_message.href, { headers }).then((r) => r.json());
```
## Good fits
* Reopening a session from your own application UI
* Letting support or operations tools inspect active work
* Handing a live session from one system step to another
* Continuing long-running work without losing context
See also: [Get Session](/rest-api/sessions/get), [List Messages](/rest-api/sessions/messages), and [Send Messages](/rest-api/sessions/follow-up).
# Workflows
Source: https://docs.blocks.team/rest-api/use-cases/workflows
Embed Blocks into existing workflow systems and continue after the final response.
Blocks fits cleanly into multi-step workflows where one stage needs an AI agent to do work and a later stage needs to consume the result. A workflow can create a session, wait for the assistant's `final_message`, and then continue with the next action in your system.
## Typical workflow shape
1. A workflow step decides work should be delegated to Blocks.
2. It creates a session with `POST /rest/v1/sessions`.
3. It stores the returned `session_id` and `_links.final_message.href`.
4. It polls or schedules a follow-up check for the final assistant message.
5. It uses that result to continue the workflow.
## Example uses
* Create a Jira or Linear issue after Blocks produces a scoped implementation plan
* Run a review or triage step, then route based on the final recommendation
* Generate a technical summary that another workflow step posts to Slack or email
* Start a coding task, then trigger downstream automation when the final answer is ready
## Example
```javascript theme={null}
const headers = {
Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}`,
"Content-Type": "application/json",
};
const session = await fetch("https://api.blocks.team/rest/v1/sessions", {
method: "POST",
headers,
body: JSON.stringify({
agent_name: "claude",
message:
"Review this ticket and produce a short implementation plan: https://linear.app/acme-corp/issue/ENG-247/add-audit-logging-to-admin-actions",
}),
}).then((r) => r.json());
const finalPage = await fetch(session._links.final_message.href, { headers }).then((r) => r.json());
const finalMessage = finalPage.items[0]?.message;
if (finalMessage) {
await continueWorkflow({
sessionId: session.id,
blocksResult: finalMessage,
});
}
```
## Why this works well
* The session ID gives every workflow run a durable reference
* `final_message` provides a clean handoff point for downstream systems
* Follow-up messages let later workflow steps continue the same session when needed
For the API details behind this pattern, start with [Quick Start](/rest-api/quick-start) and [Send Messages](/rest-api/sessions/follow-up).
# API Keys
Source: https://docs.blocks.team/sdk/api-keys
API keys are created and managed in the [dashboard](https://blocks.team/signup). They are used to authenticate requests from the Blocks CLI and are scoped to a workspace.
Install the CLI:
```bash bash theme={null}
pip install blocks-sdk
```
Configure an API key when initializing:
```bash bash theme={null}
blocks init --key
```
Manually configure an API key:
```bash bash theme={null}
blocks configure --key
```
Or simply `blocks configure` for an interactive experience.
## Expiry
API keys can be assigned a TTL (time-to-live), or never expire unless manually deleted. Expired keys will automatically be deleted. You can manually delete or rename an existing API key at any time, however you cannot modify an existing API key's TTL. An API key must be valid to register or update an agent from the CLI. Once an agent is registered, API key validity does not affect an agent's execution.
# bash
Source: https://docs.blocks.team/sdk/bash
The `bash` module can be used to execute arbitrary bash commands in your agent's runtime environment. The `bash` module is stateless, so if you need a stateful bash session, use the [experiemental\_bash](/sdk/experimental-bash) module.
## Example Usage
```python python theme={null}
from blocks import bash
email = "user@example.com"
bash(f"git config --global user.email {email}")
```
### Arguments
The bash command to execute.
If `true`, suppresses exceptions raised by the command.
# CLI
Source: https://docs.blocks.team/sdk/cli
The Blocks CLI is the primary interface for registering agents.
To get started, install the [Blocks PyPI package](https://pypi.org/project/blocks-cli/):
```bash bash theme={null}
pip install blocks-sdk
```
Once installed, you'll need to initialize Blocks. To do this, you'll need a valid API key which you can obtain from the [Blocks dashboard](https://blocks.team/signup).
```bash bash theme={null}
blocks init --key
```
This will create a `.blocks` directory in your current working directory. All agent source code needs to be placed there.
Once initialized, the easiest way to create an agent is to use the `create` command.
```bash bash theme={null}
blocks create hello_world
```
This will create a new agent in the `.blocks` directory with the following structure:
```
.blocks/
hello_world/
main.py
requirements.txt
```
If you need to change your API key at any point, you can use the `configure` command.
```bash bash theme={null}
blocks configure --key
```
Once you have an agent you'd like to register, you can use the `push` command to register it. You'll need to specify the path to the file relative to your current working directory.
For the above folder `hello_world` agent, you'd run:
```bash bash theme={null}
blocks push .blocks/hello_world/main.py
```
This may take a few minutes to complete the first time it is run, as we need to build a runtime for your agent. Subsequent pushes will not rebuild your agent's runtime unless `pip` or `plugin` dependencies have changed. Additionally, subsequent pushes for a registered agent will create a new `revision` or `version` of your agent.
Changing the `name` in the [agent decorator](/decorators/agent) will create a new agent.
# config
Source: https://docs.blocks.team/sdk/config
The `config` module is used in cases where you need to override the default Github token and repository path associated with the event payload.
By default, a `GITHUB_TOKEN` with permissions for all repositories enabled in the Github app installation is injected into an agent's runtime environment.
## Example Usage
```python python theme={null}
from blocks import config
config.set_github_token("your-github-token")
config.set_github_repository_path("your-github-repository-path") # Example: "YourOrg/repo-name"
```
## Methods
### set\_github\_token
```python python theme={null}
config.set_github_token(token)
```
Sets the `GITHUB_TOKEN` environment variable.
The GitHub token to use.
### set\_github\_repository\_path
```python python theme={null}
config.set_github_repository_path(path)
```
Sets the `GITHUB_REPOSITORY_PATH` environment variable.
The GitHub repository path to use. For example, `YourOrg/repo-name`.
# experimental_bash
Source: https://docs.blocks.team/sdk/experimental-bash
The `experimental_bash` module is for when you need to create a persistent and stateful bash session. For one-off stateless bash commands, you can use the [bash](/sdk/bash) module instead.
## Example Usage
```python theme={null}
from blocks import experimental_bash
experimental_bash("cd ../")
experimental_bash("mkdir new_dir")
```
### Arguments
The bash command to execute.
If `true`, suppresses exceptions raised by the command.
# git
Source: https://docs.blocks.team/sdk/git
The `git` module is a lightweight wrapper around common git operations. For more complex operations, you can execute commands directly using the `bash` utility. Credentials are automatically configured by default.
Default configuration:
```bash ~/.gitconfig theme={null}
user.name=BlocksOrg
user.email=bot@blocks.team
remote.origin.url=git@github.com:YourOrg/repo.git # The remote url of the repo which triggered the agent
```
## Example Usage
```python python theme={null}
from blocks import git
# Clone a repository
git.clone(target_dir="my-project", ref="develop")
# Make changes and commit
git.add(all=True)
git.commit("Update README.md")
# Push changes to origin
git.push(publish=True)
```
## Methods
### add
```python python theme={null}
git.add(file, all=False)
```
Stages a file or files for commit. If all is True, stages all changes.
File path to add (ignored if all=True).
If True, adds all changes instead of a single file.
### branch
```python python theme={null}
git.branch(branch_name, checkout=False)
```
Creates a new branch by default. If checkout is True, creates and checks out (git checkout -b branch-name), otherwise just creates a branch with branch-name.
The name of the new branch.
Whether to create and checkout the branch.
### checkout
```python python theme={null}
git.checkout(target_dir="repo", ref="", new_branch=False)
```
Clones a repository into `target_dir`. If `ref` is provided, it checks out that specific branch/tag.
The local directory for the clone.
The Git ref (branch, tag) to clone. Defaults to "" (clone the default branch).
If True, this indicates the intention to create a new branch, but is not currently used in the command.
### clone
```python python theme={null}
git.clone(target_dir="repo", ref="", new_branch=False)
```
An alias for checkout, providing the same behavior for consistency.
The local directory for the clone.
The Git ref (branch, tag) to clone. Defaults to "" (clone the default branch).
If True, this indicates the intention to create a new branch, but is not currently used in the command.
### commit
```python python theme={null}
git.commit(message)
```
Commits staged changes with a given commit message.
The commit message.
### init
```python python theme={null}
git.init()
```
Initializes a new Git repository locally.
### pull
```python python theme={null}
git.pull()
```
Pulls from the repository specified by the class’s constructed remote URL. It runs git pull origin=url HEAD.
### push
```python python theme={null}
git.push(publish=False)
```
Pushes the current HEAD to the remote. If publish is True, it pushes with the -u origin HEAD flag, setting the upstream branch.
Whether to set upstream on push.
# Installation
Source: https://docs.blocks.team/sdk/installation
## Prerequisites
* An environment with [Python 3.11](https://www.python.org/downloads/) and [Node.js 22](https://nodejs.org/en/download) installed.
* [Blocks account and API key](https://blocks.team/signup).
If you'd like to use Blocks with other providers such as GitLab or Bitbucket, please reach out: [dev@blocks.team](mailto:dev@blocks.team).
```bash bash theme={null}
pip install blocks-sdk
```
```bash bash theme={null}
blocks init --key
```
We'll verify your API key and create a `.blocks` directory in the current working directory.
The easiest way to create an agent is to use the `create` command.
```bash bash theme={null}
blocks create ci-ralph-loop
```
This will create a new agent in the `.blocks` directory with the following structure:
```
.blocks/
ci-ralph-loop/
main.py
requirements.txt
```
Below is an example you can copy to get started. It runs a "Ralph" loop on every pull request: a coding agent watches a CI workflow, and if it's failing, it diagnoses the root cause, makes the smallest fix on a dedicated branch, and keeps iterating until CI is green.
```bash ./blocks/ci-ralph-loop/main.py theme={null}
import os
import sys
from blocks_control_sdk.constants.openai import OpenAIModels, OpenAIAuthenticationMode
from blocks_control_sdk.constants.core import WORKSPACE_DIR
from blocks import on, task
from blocks_control_sdk import Codex, CodexAgentConfig
WORKFLOW_NAME = "Unit Test" # CI workflow we want to get green
MAX_ITERATIONS = 5
def done(response):
return "" in response
@on("github.pull_request")
@task(name="ci-ralph-loop")
def ci_ralph_loop(input):
owner, repo = input["owner"], input["repo"]
branch_name = input["ref"]
pr_number = (input.get("pull_request") or {}).get("number")
# Avoid recursion: skip if we're already on our own fix branch
if not branch_name or branch_name.endswith("-ralphci"):
print("Loop already running, exiting.")
return
fix_branch = f"{branch_name}-ralphci"
memory = WORKSPACE_DIR.absolute() / "MEMORY.md"
os.chdir(WORKSPACE_DIR.absolute())
# Spin up the coding agent
agent = Codex()
agent.init(CodexAgentConfig(
model=OpenAIModels.gpt_5_3_codex,
authentication_mode=OpenAIAuthenticationMode.oauth,
))
context = f"PR #{pr_number} on {owner}/{repo}, branch '{branch_name}', fix branch '{fix_branch}'."
def check_prompt(branch):
return f"""{context}
Wait for the "{WORKFLOW_NAME}" CI workflow on branch "{branch}" to finish.
- If it passes (or doesn't exist), reply with .
- If it fails, summarize which tests failed."""
fix_prompt = f"""{context}
The "{WORKFLOW_NAME}" workflow is failing. You run in a loop with fresh context
each time, so {memory} is your only record of past attempts.
1. Read {memory}; don't retry anything already marked as failed there.
2. Find the root cause. If it's transient infra (not a code bug), note it in
{memory} and reply with .
3. Otherwise make the smallest fix and append an attempt note to {memory}.
4. Commit only the fix to '{fix_branch}' (create + open a PR if needed), push it."""
branch = branch_name
for i in range(MAX_ITERATIONS):
print(f"--- Iteration {i}: checking {branch} ---")
# Fresh Context window
agent.new_chat_thread(new_session=True)
if done(status := agent.query_sync_beta(check_prompt(branch))):
print("CI is green. Done.")
return
print(status)
print(f"--- Iteration {i}: fixing -> {fix_branch} ---")
# Fresh Context window
agent.new_chat_thread(new_session=True)
if done(agent.query_sync_beta(fix_prompt)):
print("Failure not actionable (transient). Exiting.")
return
branch = fix_branch # now watch CI on the fix branch
print(f"Exhausted {MAX_ITERATIONS} attempts.")
sys.exit(1)
```
Declare your Python dependencies in a `requirements.txt` and any MCP servers or CLI tools in a `package.json` alongside `main.py`. Adding these files automatically installs the dependencies when the agent image is built.
```bash ./blocks/ci-ralph-loop/requirements.txt theme={null}
blocks-control-sdk>=0.2.2,<0.3.0
blocks-sdk>=0.1.81,<0.2.0
requests
litellm>=1.61.16,<=1.74.8
slack-sdk>=3.19.2
fastmcp
tomlkit>=0.13.3,<1.0.0
openai<=1.99.9
jinja2>=3.1.0
```
```json ./blocks/ci-ralph-loop/package.json theme={null}
{
"dependencies": {
"@upstash/context7-mcp": "^1.0.16",
"firecrawl-mcp": "^2.0.2",
"mcp-remote": "^0.1.29",
"@modelcontextprotocol/server-slack": "2025.4.25",
"@playwright/mcp": "^0.0.36",
"@tacticlaunch/mcp-linear": "^1.0.11",
"playwright": "^1.55.0",
"@anthropic-ai/claude-code": "2.1.117",
"@openai/codex": "0.135.0"
}
}
```
Agents are registered with the `push` command; specify the filename relative to your current working directory. All agents defined the file will be registered, however you can only register one file at a time.
```bash bash theme={null}
blocks push .blocks/ci-ralph-loop/main.py
```
Image builds may take up to 10 minutes. They only happen once — subsequent pushes reuse the cached image unless your `npm` or `pip` dependencies change.
## Project Structure
The `.blocks` directory is where agent source code is defined. A typical project structure looks like the following:
```
.blocks/
agent-1/
main.py
requirements.txt
agent-2/
main.py
requirements.txt
agent-3.py
```
Dependencies are isolated to each agent, and there are no restrictions for supported `pip` packages.
## Version Control
Ideally, your agents will be checked into some git provider for version control and storage, just like any other source code. We do preserve the state of registered agents, but do not implement git for version control. However, this is something we can add if requested.
## Where do I get an API key?
API keys are created and managed in the [dashboard](https://blocks.team/signup).
# repo
Source: https://docs.blocks.team/sdk/repo
The `repo` module is an interface for performing actions onto git providers. Reply to a comment, create an issue, review a pull request, open a pull request, etc.
## Example Usage
```python python theme={null}
from blocks import repo
# Create a new issue
repo.create_issue(
title="Update README.md",
body="Update the README.md file",
state="open",
target_branch="main"
)
# Update an issue
repo.update_issue(
issue_number=1,
title="Update README.md",
body="Update the README.md file",
state="open",
target_branch="main"
)
# Comment on a pull request
repo.comment_on_pull_request(
pull_request_number=1,
body="Update the README.md file",
owner="BlocksOrg",
repo="blocks"
)
```
## Methods
### update\_pull\_request
Updates a pull request. Typically, you'd use this to update the title, body/description, or state of a pull request.
```python python theme={null}
repo.update_pull_request(
pull_request_number=1,
title="Update README.md",
body="Some description",
)
```
The number of the pull request to update.
The title of the pull request.
The body of the pull request.
Whether the maintainer can modify the pull request.
The state of the pull request.
The pull request is open.
The pull request is closed.
The target branch of the pull request.
The owner of the repository.
The name of the repository.
### update\_issue
Updates an issue. Typically, you'd use this to update the title, description, or state of an issue.
```python python theme={null}
repo.update_issue(
issue_number=1,
title="Update README.md",
description="Update the README.md file",
)
```
The number of the issue to update.
The title of the issue.
The body of the issue.
The state of the issue.
The issue is open.
The issue is closed.
The reason the issue is in the state it is in.
The issue is completed.
The issue is not planned.
The issue is reopened.
The number of the milestone to assign to the issue.
The target branch of the issue.
The owner of the repository.
The name of the repository.
### create\_issue
Creates an issue.
```python python theme={null}
repo.create_issue(
title="Update README.md",
body="Some description",
)
```
The title of the issue.
The body of the issue.
A list of assignees to assign to the issue. Corresponds to arrays of `user.login`.
A list of labels to add to the issue.
The number of the milestone to assign to the issue.
The owner of the repository.
The name of the repository.
### create\_pull\_request
Creates a pull request.
```python python theme={null}
repo.create_pull_request(
source_branch="main",
target_branch="develop",
title="Update README.md",
body="Some description"
)
```
The source branch to create the pull request from.
The target branch to create the pull request to.
The title of the pull request.
The body of the pull request.
Whether the pull request is a draft.
The number of the issue to create the pull request from.
The owner of the repository.
The name of the repository.
### comment\_on\_pull\_request
Comments on a pull request.
```python python theme={null}
repo.comment_on_pull_request(
pull_request_number=1,
body="Update the README.md file",
)
```
The number of the pull request to comment on.
The body of the comment.
The owner of the repository.
The name of the repository.
### delete\_pull\_request\_comment
Deletes a pull request comment.
```python python theme={null}
repo.delete_pull_request_comment(
comment_id=1
)
```
The ID of the comment to delete.
The owner of the repository.
The name of the repository.
### update\_pull\_request\_comment
```python python theme={null}
repo.update_pull_request_comment(
comment_id=1,
body="Some description"
)
```
Updates a pull request comment.
The ID of the comment to update.
The body of the comment.
The owner of the repository.
The name of the repository.
### comment\_on\_pull\_request\_file
Comments on a file in a pull request.
```python python theme={null}
repo.comment_on_pull_request_file(
commit_id="1234567890",
file_path="README.md",
pull_request_number=1,
body="Update the README.md file",
position=1,
)
```
The SHA of the commit to comment on.
The path of the file to comment on.
The position of the comment in the file.
The number of the pull request to comment on.
The body of the comment.
The line number to comment on.
The ID of the comment to reply to.
The side of the comment in the file.
The comment is on the left side of the file.
The comment is on the right side of the file.
The start line of the comment in the file.
The side of the comment in the file.
The comment is on the left side of the file.
The comment is on the right side of the file.
The comment is on the side of the file.
The type of the subject.
The comment is on a line.
The comment is on a file.
The owner of the repository.
The name of the repository.
### update\_issue\_comment
Updates an issue comment.
```python python theme={null}
repo.update_issue_comment(
comment_id=1,
body="Update the README.md file"
)
```
The ID of the comment to update.
The body of the comment.
The owner of the repository.
The name of the repository.
### delete\_issue\_comment
Deletes an issue comment.
```python python theme={null}
repo.delete_issue_comment(
comment_id=1
)
```
The ID of the comment to delete.
The owner of the repository.
The name of the repository.
### comment\_on\_issue
Comments on an issue.
```python python theme={null}
repo.comment_on_issue(
issue_number=1,
body="Update the README.md file"
)
```
The number of the issue to comment on.
The body of the comment.
The owner of the repository.
The name of the repository.
### reply\_to\_pull\_request\_comment
Replies to a pull request comment.
```python python theme={null}
repo.reply_to_pull_request_comment(
reply_to_id=1,
pull_request_number=1,
body="Some description"
)
```
The ID of the comment to reply to.
The number of the pull request to reply to.
The body of the reply.
The owner of the repository.
The name of the repository.
### review\_pull\_request
Reviews a pull request.
```python python theme={null}
repo.review_pull_request(
pull_request_number=1,
body="Update the README.md file",
commit_id="1234567890",
comments=[],
event="COMMENT",
)
```
The number of the pull request to review.
The body of the review.
The SHA of the commit to review.
A list of comments to add to the review.
The relative path to the file that necessitates a review comment.
The body of the comment.
The line number to review.
The side of the comment in the file.
The start line of the comment in the file.
The position in the diff where you want to add a review comment. Note this value is not the same as the line number in the file. The position value equals the number of lines down from the first "@@" hunk header in the file you want to add a comment. The line just below the "@@" line is position 1, the next line is position 2, and so on. The position in the diff continues to increase through lines of whitespace and additional hunks until the beginning of a new file.
The event to trigger on the review.
The review is approved.
The review is requested changes.
The review is a comment.
The owner of the repository.
The name of the repository.
# Claude Code
Source: https://docs.blocks.team/using-blocks/agents/claude-code
The go-to coding agent for many developers
## Overview
Claude Code is powered by Anthropic's Claude models and is the go-to coding agent for many developers. It offers excellent performance across a wide range of tasks.
## Latest Models
* **Claude 4.5 Sonnet** - Latest high-performance model
* **Claude 4.5 Opus** - Most capable model with enhanced reasoning
## Authentication
Claude Code supports three authentication methods:
**Pay-as-you-go API access**
1. Get an API key at [Anthropic Console](https://console.anthropic.com/)
2. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
3. Select **Claude Code** and choose "API Key"
4. Paste your API key (starts with `sk-ant-`)
**Connect your Claude Pro/Team subscription**
1. Go to [Blocks Dashboard](https://blocks.team) → **Dashboard** → **Agents**
2. Select **Claude Code** and choose "Connect Subscription"
3. Authorize Blocks to use your Anthropic subscription
This uses your subscription credits instead of pay-as-you-go billing.
**Enterprise AWS Bedrock access**
1. Set up Claude models in AWS Bedrock
2. Generate Bedrock API credentials
3. In Blocks Dashboard → **Dashboard** → **Agents** → **Claude Code**
4. Select "AWS Bedrock" and enter your credentials
## Setting as Default
To use Claude Code automatically for all requests:
1. Go to **Dashboard** → **Agents**
2. Select "Set as default agent" from Claude Code's dropdown menu
## Using Claude Code
Once configured, Claude Code is used automatically if it's your default agent:
```
@blocks can you review this code?
```
Or use the `/claude` slash command to use it specifically:
```
@blocks /claude review this PR for security issues
```
## Best For
* Code reviews and analysis
* Complex problem solving
* Architecture planning
* Security analysis
* Detailed explanations
* Refactoring
## Cost
Claude Code uses your Anthropic API key or subscription:
* Check pricing at [Anthropic Pricing](https://www.anthropic.com/pricing)
* Monitor usage in [Anthropic Console](https://console.anthropic.com/)
* API usage is billed by Anthropic based on tokens
## Next Steps
Learn about Codex
Explore Gemini CLI (experimental)
# Codex
Source: https://docs.blocks.team/using-blocks/agents/codex
High-reasoning capabilities for complex tasks and critical reviews
## Overview
Codex is powered by OpenAI's models and offers high-reasoning capabilities for complex tasks and critical reviews.
## Latest Model
* **GPT 5.2** - OpenAI's latest model with advanced reasoning capabilities
## Authentication
Codex supports two authentication methods:
**Pay-as-you-go API access**
1. Get an API key at [OpenAI Platform](https://platform.openai.com/api-keys)
2. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
3. Select **Codex** and choose "API Key"
4. Paste your API key (starts with `sk-`)
**Connect your ChatGPT Plus/Team subscription**
1. Go to [Blocks Dashboard](https://blocks.team) → **Dashboard** → **Agents**
2. Select **Codex** and choose "Connect Subscription"
3. Authorize Blocks to use your OpenAI subscription
This uses your subscription credits instead of pay-as-you-go billing.
## Setting as Default
To use Codex automatically for all requests:
1. Go to **Dashboard** → **Agents**
2. Select "Set as default agent" from Codex's dropdown menu
## Using Codex
Once configured, Codex is used automatically if it's your default agent:
```
@blocks can you implement this feature?
```
Or use the `/codex` slash command to use it specifically:
```
@blocks /codex implement this API endpoint
```
## Best For
* Complex reasoning tasks
* Critical code reviews
* Advanced problem solving
* Detailed analysis
* Architecture decisions
* In-depth explanations
## Cost
Codex uses your OpenAI API key or subscription:
* Check pricing at [OpenAI Pricing](https://openai.com/pricing)
* Monitor usage in [OpenAI Dashboard](https://platform.openai.com/usage)
* API usage is billed by OpenAI based on tokens
## Next Steps
Learn about Claude Code
Explore Gemini CLI (experimental)
# Cursor CLI
Source: https://docs.blocks.team/using-blocks/agents/cursor-cli
AI-first code editor built on VS Code with parallel agent support
## Overview
Cursor CLI is an AI-first code editor built on VS Code, trusted by Fortune 500 companies for its advanced capabilities. It features the proprietary Cursor 2.0 model alongside support for GPT-5, Claude 4.5 models, Gemini 3 Pro, and Grok Code. The Composer feature enables instant code generations with parallel agent support for complex tasks.
## Latest Models
Cursor CLI provides access to cutting-edge models:
* **Cursor 2.0** - Proprietary model optimized for code editing
* **GPT-5** - OpenAI's latest flagship model
* **Claude 4.5 Sonnet** - Anthropic's high-performance model
* **Claude 4.5 Opus** - Anthropic's most capable model
* **Gemini 3 Pro** - Google's advanced reasoning model
* **Grok Code** - Specialized code generation model
Cursor's Composer feature allows multiple agents to work in parallel, significantly speeding up complex multi-file changes.
## Authentication
Cursor CLI requires a Cursor API key:
**Get your Cursor API key**
1. Sign up at [Cursor](https://cursor.com/)
2. Subscribe to Cursor Pro or Team plan
3. Generate an API key in Cursor settings
4. Set the environment variable: `CURSOR_API_KEY=cursor-...`
5. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
6. Select **Cursor CLI** and paste your API key
Cursor API keys are available with Cursor Pro (\$20/month) or Team plans. The API includes access to all supported models.
## Using Cursor CLI
Use the `/cursor` slash command to invoke Cursor CLI:
```
@blocks /cursor refactor this component with parallel agents
@blocks /cursor implement this feature using Cursor 2.0
```
Cursor CLI must be explicitly invoked with the `/cursor` command and cannot be set as your default agent.
## Best For
* VS Code integration (familiar interface)
* Parallel agent workflows (multiple agents working together)
* Instant code generations with Composer
* Fortune 500-grade reliability
* Complex multi-file refactoring
* Teams already using VS Code
* Access to multiple premium models in one subscription
## Cost
Cursor CLI uses your Cursor subscription:
* Cursor Pro: \$20/month (includes API access)
* Cursor Team: Custom pricing for organizations
* Check [Cursor Pricing](https://cursor.com/pricing) for details
* All models included in subscription (no per-token billing)
* Monitor usage in Cursor dashboard
Unlike pay-as-you-go API services, Cursor uses a subscription model with unlimited usage, making costs predictable.
## Next Steps
Learn about Claude Code
Explore OpenCode's multi-provider support
# Gemini CLI
Source: https://docs.blocks.team/using-blocks/agents/gemini-cli
Best for simple tasks (Experimental)
Gemini CLI is currently **experimental** and may have different behavior or limitations compared to the stable agents (Claude Code and Codex).
## Overview
Gemini CLI is powered by Google's Gemini models and is best for simple tasks.
Gemini CLI **cannot be set as a default agent**. You must use the `/gemini` slash command to invoke it.
## Latest Model
* **Gemini 3 Pro** - Google's latest model with large context capabilities
## Authentication
Gemini CLI requires a Google AI API key:
1. Get an API key at [Google AI Studio](https://aistudio.google.com/app/apikey)
2. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
3. Select **Gemini CLI** and choose "API Key"
4. Paste your Google AI API key
Use Google AI Studio (not Google Cloud) to get your API key.
## Using Gemini CLI
Since Gemini CLI cannot be set as default, you must use the `/gemini` slash command:
```
@blocks /gemini analyze this codebase
```
## Best For
* Simple tasks
## Cost
Gemini CLI uses your Google AI API key:
* Check pricing at [Google AI Pricing](https://ai.google.dev/pricing)
* Google AI offers a generous free tier
* Monitor usage in [Google AI Studio](https://aistudio.google.com/)
* API usage is billed by Google based on tokens
## Experimental Status
As an experimental agent, Gemini CLI:
* May have occasional unexpected behavior
* Features and capabilities may change
* Is less battle-tested than Claude Code or Codex
For production-critical tasks, consider using [Claude Code](/using-blocks/agents/claude-code) or [Codex](/using-blocks/agents/codex) which are more stable.
## Next Steps
Learn about Claude Code (stable)
Learn about Codex (stable)
Work across repositories
# Kimi Code
Source: https://docs.blocks.team/using-blocks/agents/kimi-code
Coding agent powered by Moonshot AI's Kimi models
## Overview
Kimi Code is a coding agent powered by Moonshot AI's Kimi models. It offers strong performance on coding tasks and is a great option for teams looking to diversify beyond Anthropic and OpenAI providers.
## Authentication
Kimi Code requires a Kimi API key:
1. Get an API key from [Moonshot AI Platform](https://platform.moonshot.cn/)
2. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
3. Select **Kimi Code** and choose "API Key"
4. Paste your Kimi API key (`KIMI_API_KEY`)
## Setting as Default
To use Kimi Code automatically for all requests:
1. Go to **Dashboard** → **Agents**
2. Select "Set as default agent" from Kimi Code's dropdown menu
## Using Kimi Code
Once configured, Kimi Code is used automatically if it's your default agent:
```
@blocks can you review this code?
```
Or use the `/kimi` slash command to use it specifically:
```
@blocks /kimi implement this feature
```
```
@blocks /kimi review this PR for potential bugs
```
## Best For
* Coding tasks requiring a strong alternative to Anthropic and OpenAI models
* Teams wanting to diversify AI providers
* Cost optimization with a competitive pricing model
## Cost
Kimi Code uses your Kimi API key and is billed directly by Moonshot AI. Check pricing at the [Moonshot AI Platform](https://platform.moonshot.cn/).
## Next Steps
Learn about Claude Code
Explore OpenCode capabilities
# OpenCode
Source: https://docs.blocks.team/using-blocks/agents/opencode
Open source agent supporting 75+ LLM providers with privacy-focused architecture
## Overview
OpenCode is an open source coding agent that supports 75+ LLM providers. In Blocks, OpenCode currently supports Anthropic (Claude models) and OpenAI (GPT models), with additional provider support planned. With over 650,000 monthly users, OpenCode offers a privacy-focused approach where no code is stored on external servers.
OpenCode is currently **experimental** and may have different behavior or limitations compared to stable agents like Claude Code or Codex.
## Latest Models
OpenCode provides access to multiple providers through its curated "Zen" model service. In Blocks, the following providers are currently supported:
* **OpenAI**: GPT-5, GPT-4o, GPT-4 Turbo
* **Anthropic**: Claude 4.5 Opus, Claude 4.5 Sonnet
* **Moonshot AI**: Kimi models (via `KIMI_API_KEY`)
While OpenCode supports 75+ providers including Google, AWS Bedrock, Groq, OpenRouter, and Azure, Blocks currently supports Anthropic, OpenAI, and Kimi API keys. Additional provider support is coming soon.
## Authentication
OpenCode supports multiple authentication methods depending on your provider. In Blocks, the following providers are supported:
**Use Anthropic models (Claude 4.5 Opus, Sonnet)**
1. Get an API key at [Anthropic Console](https://console.anthropic.com/)
2. Set the environment variable: `ANTHROPIC_API_KEY=sk-ant-...`
3. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
4. Select **OpenCode** and choose "Anthropic"
5. Paste your API key
**Use OpenAI models (GPT-5, GPT-4o)**
1. Get an API key at [OpenAI Platform](https://platform.openai.com/api-keys)
2. Set the environment variable: `OPENAI_API_KEY=sk-...`
3. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
4. Select **OpenCode** and choose "OpenAI"
5. Paste your API key
**Use Moonshot AI's Kimi models**
1. Get an API key at [Moonshot AI Platform](https://platform.moonshot.cn/)
2. Set the environment variable: `KIMI_API_KEY=...`
3. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
4. Select **OpenCode** and choose "Kimi"
5. Paste your API key
**Currently limited to Anthropic, OpenAI, and Kimi**
While OpenCode supports 75+ providers (Google AI, Groq, Azure, OpenRouter, AWS Bedrock, etc.), Blocks currently only supports authentication via:
* `ANTHROPIC_API_KEY` (for Claude models)
* `OPENAI_API_KEY` (for GPT models)
* `KIMI_API_KEY` (for Kimi models)
Support for additional providers (Google, Groq, Azure, OpenRouter, etc.) is planned for future releases.
## Setting as Default
To use OpenCode automatically for all requests:
1. Go to **Dashboard** → **Agents**
2. Select "Set as default agent" from OpenCode's dropdown menu
## Using OpenCode
Once configured, OpenCode is used automatically if it's your default agent:
```
@blocks can you review this code?
```
Or use the `/opencode` slash command to use it specifically:
```
@blocks /opencode implement this feature using GPT-5
```
## Best For
* Flexibility between OpenAI, Anthropic, and Kimi models
* Privacy-focused development (no code storage)
* Open source transparency
* Cost optimization (switch between providers based on task complexity)
* Experimentation with different models from major providers
* Teams wanting to avoid vendor lock-in
Currently supports Anthropic, OpenAI, and Kimi providers in Blocks. Support for additional providers (Google, Groq, Azure, etc.) is coming soon.
## Cost
OpenCode costs vary by provider. Currently supported in Blocks:
* **Anthropic**: Check [Anthropic Pricing](https://www.anthropic.com/pricing) for Claude models
* **OpenAI**: Check [OpenAI Pricing](https://openai.com/pricing) for GPT models
* **Moonshot AI**: Check [Moonshot AI Platform](https://platform.moonshot.cn/) for Kimi pricing
Monitor usage in each provider's console. API usage is billed directly by your chosen provider.
You can switch between Anthropic, OpenAI, and Kimi to optimize costs based on task complexity. Use the provider that best fits your needs and budget for each task.
## Next Steps
Learn about Claude Code
Explore Codex capabilities
# Sisyphus (Oh My OpenCode)
Source: https://docs.blocks.team/using-blocks/agents/sisyphus
Multi-agent orchestration system with specialized agents for complex tasks
## Overview
Sisyphus, powered by Oh My OpenCode, is an advanced orchestration layer that manages multiple specialized agents working together. It features a main conductor agent (Claude Opus 4.5) coordinating with specialized agents including Oracle (GPT-5) for strategic planning and architecture decisions. Sisyphus excels at complex multi-agent workflows.
Sisyphus is currently **experimental** and uses sophisticated multi-agent orchestration that may behave differently than single-agent systems.
## Latest Models
Sisyphus uses a multi-agent architecture with specialized models currently supported in Blocks:
* **Main Conductor**: Claude Opus 4.5 - Orchestrates all agents and handles coordination
* **Oracle Agent**: GPT-5 - Strategic planning and architecture decisions
* **Backend Specialist**: Claude Sonnet 4.5 - API and database work
Each agent specializes in specific tasks. The Main Conductor intelligently delegates work to the most appropriate specialist agent, enabling parallel execution and domain expertise. Additional specialized agents may be added as more providers become available in Blocks.
## Multi-Agent Orchestration
Sisyphus's power comes from coordinated agent collaboration:
1. **Task Analysis**: Main Conductor analyzes the request
2. **Work Distribution**: Tasks are delegated to specialist agents
3. **Parallel Execution**: Multiple agents work simultaneously
4. **Quality Review**: Oracle validates architectural decisions
5. **Integration**: Main Conductor synthesizes results
This approach is particularly effective for:
* Large refactoring across multiple files
* Full-stack feature implementation
* Architecture design with code generation
* Complex debugging requiring multiple perspectives
## Authentication
Sisyphus requires API keys for the underlying model providers:
**Required for Main Conductor and Backend Specialist**
1. Get an API key at [Anthropic Console](https://console.anthropic.com/)
2. Set the environment variable: `ANTHROPIC_API_KEY=sk-ant-...`
3. In [Blocks Dashboard](https://blocks.team), go to **Dashboard** → **Agents**
4. Select **Sisyphus** and configure Anthropic authentication
**Required for Oracle Agent**
1. Get an API key at [OpenAI Platform](https://platform.openai.com/api-keys)
2. Set the environment variable: `OPENAI_API_KEY=sk-...`
3. Configure in Blocks Dashboard → **Agents** → **Sisyphus**
4. Enable Oracle agent for strategic planning tasks
**Recommended for full capabilities**
For the complete Sisyphus experience, configure both:
* `ANTHROPIC_API_KEY` for Claude models
* `OPENAI_API_KEY` for Oracle agent
This enables all specialist agents and provides maximum flexibility in task delegation.
## Using Sisyphus
Sisyphus is experimental and cannot be set as a default agent. Use the `/sisyphus` slash command to invoke it:
```
@blocks /sisyphus implement a full-stack feature with tests and docs
@blocks /sisyphus refactor the entire auth system
```
Sisyphus shines with complex requests like "refactor the entire auth system" or "add a new feature with frontend, backend, tests, and documentation." The multi-agent system handles complexity better than single agents.
As an experimental agent, Sisyphus must be explicitly invoked with the `/sisyphus` command and cannot be used as your default agent.
## Best For
* Complex multi-file refactoring
* Full-stack feature implementation
* Architecture design and planning
* Large-scale codebase transformations
* Projects requiring multiple areas of expertise
* Strategic code reviews with Oracle agent validation
* Claude Code compatibility (drop-in replacement)
## Cost
Sisyphus uses multiple API providers currently supported in Blocks:
* **Anthropic API**: Billed for Claude Opus 4.5 and Sonnet 4.5 usage
* **OpenAI API**: Billed for GPT-5 (Oracle agent) usage
Check pricing at:
* [Anthropic Pricing](https://www.anthropic.com/pricing)
* [OpenAI Pricing](https://openai.com/pricing)
Sisyphus may use more tokens than single-agent systems due to multi-agent coordination. However, it often completes complex tasks faster and with higher quality, potentially reducing overall cost through efficiency.
## Experimental Status
As an experimental feature, Sisyphus is actively evolving:
* Multi-agent orchestration is a cutting-edge approach
* Agent delegation strategies are being refined
* New specialist agents may be added
* Performance optimizations are ongoing
* Feedback helps improve the system
The orchestration layer represents the future of AI coding assistants, where specialized expertise combines for superior results.
## Next Steps
Learn about Claude Code compatibility
Explore OpenCode's architecture
# Custom MCP
Source: https://docs.blocks.team/using-blocks/features/custom-mcp
Add custom MCP servers to your workspace and choose which agents can use them
## Overview
Custom MCP (Model Context Protocol) servers extend what coding agents can do by giving them access to external tools and data sources. Add the MCP server once at the workspace level, define it with a JSON configuration object, reference global environment variables for secrets, and then choose which agents can use it.
Configure custom MCP servers at **Dashboard → Settings → MCP Servers**.
MCP servers are workspace-wide and can be enabled or disabled per agent.
## Add a custom MCP server
From **Settings → MCP Servers**, scroll to **Custom Servers** and click **Add MCP**. The create dialog includes two parts:
* **Environment variables** — copy `${env:VARIABLE_NAME}` references for global secrets you already saved in the workspace.
* **MCP Configuration JSON** — paste the MCP server config that Blocks should run for agents.
If the workspace has no global environment variables yet, the dialog shows a message with a **Create environment variable** button. Use it to go to **Settings → Environment Variables**, add the secret in the **Global** namespace, then return to the MCP server dialog.
See [Environment Variables](/using-blocks/features/environment-variables) for the full variable flow.
## Reference environment variables
Use `${env:VARIABLE_NAME}` in `env` values anywhere the MCP config needs a secret value. Blocks resolves the value at runtime before starting the MCP server, so the actual secret does not need to be pasted into the JSON.
```json theme={null}
{
"command": "npx",
"args": ["-y", "@my-org/my-mcp-server"],
"env": {
"API_KEY": "${env:MY_VARIABLE}",
"BASE_URL": "${env:MY_SERVICE_URL}"
}
}
```
When global environment variables exist, open the selector, search for the variable, and click the variable row or copy icon. The selector lists your global environment variables and copies the formatted `${env:...}` value for you.
Only global environment variables can be referenced from custom MCP configs. Repository-scoped variables are not available here. Keep secrets in **Settings → Environment Variables** and reference them from the JSON instead of hardcoding tokens, keys, or connection strings.
## Configuration format
Each custom MCP server is defined as a JSON object.
| Field | Required | Description |
| --------- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
| `command` | Yes | The executable Blocks should run, such as `npx`, `node`, or `python`. |
| `args` | No | Arguments passed to the command. |
| `env` | No | Environment variables passed to the MCP server process. Use `${env:VARIABLE_NAME}` to reference saved global variables. |
Blocks validates the JSON before saving. If you paste multiple named MCP configs, Blocks detects them and lets you review the generated server names before saving.
## Choose agent access
After saving a custom MCP server, open its settings page to edit the JSON configuration and choose which agents can use it. Toggle on the agents that should have access.
Agent access is workspace-wide. Enable only the agents that need the MCP server so unrelated agents do not receive extra tools.
## Simple workflow
1. Create any needed secrets in **Settings → Environment Variables** using the **Global** namespace.
2. Go to **Settings → MCP Servers** and click **Add MCP**.
3. Copy environment variable references from the selector and paste them into the MCP JSON.
4. Save the server.
5. Open the server settings and enable the agents that should use it.
## Example configurations
### Internal tool with authentication
```json theme={null}
{
"command": "node",
"args": ["/usr/local/bin/my-internal-tool"],
"env": {
"AUTH_TOKEN": "${env:INTERNAL_TOOL_TOKEN}",
"API_BASE": "${env:INTERNAL_TOOL_URL}"
}
}
```
### Python-based MCP server
```json theme={null}
{
"command": "python",
"args": ["-m", "my_mcp_package"],
"env": {
"SECRET_KEY": "${env:MY_SECRET_KEY}"
}
}
```
# Environment Variables
Source: https://docs.blocks.team/using-blocks/features/environment-variables
Manage secrets and configuration available to coding agents at runtime
## Overview
Environment variables let you provide secrets, API keys, and configuration values to coding agents without hardcoding them in your repositories. Variables are stored securely and injected into the agent sandbox at runtime.
Manage them at **Dashboard → Settings → Environment Variables**.
## Scoping variables
Variables can be scoped **globally** (available in every session) or to a **specific repository** (only available when that repo is active). Use the namespace selector at the top of the page to switch between global and per-repo scopes when creating or viewing variables.
Use per-repo scoping when different repositories need different values for the same key — for example, separate database URLs for different services.
## Adding variables
**Single variable** — click **Add**, enter a name and value, and save.
**Bulk import** — switch to the **Bulk** tab and paste multiple `KEY=VALUE` pairs, one per line. Lines starting with `#` are treated as comments. You'll see a confirmation showing which variables will be created and which will be updated before anything is saved.
Variable names must be alphanumeric with underscores only (max 100 characters). Values are masked after saving and never displayed in plaintext.
## Editing and deleting
Click any variable row to open the edit dialog. Use the eye icon to reveal the current value. To delete, open the three-dot menu and select **Delete** — you'll be asked to confirm before anything is removed.
# Global Configuration
Source: https://docs.blocks.team/using-blocks/features/global-configuration
Load shared skills, hooks, and sub-agents into every coding agent session from a central repository
## Overview
A global configuration repository lets you maintain shared skills, hooks, and sub-agents in one place and have them automatically loaded into every coding agent session across your workspace — regardless of which repository the agent is working in.
Configure it at **Dashboard → Settings → Global Configuration**.
## What gets loaded
When a session starts, Blocks loads the following from your global configuration repository:
* **Skills** from `.claude/skills/`, `.codex/skills/`, and `.agents/skills/`
* **Hooks** for customizing agent behavior
* **Sub-agents** configuration
These are merged with skills from the active repository and any skills created directly in the dashboard. See [Skills](/using-blocks/features/skills) for how merging works.
## Setting a global configuration repository
1. Go to **Dashboard → Settings → Global Configuration**
2. Select a repository from the dropdown — only repositories already connected to your workspace are shown
3. Click **Save**
To remove the global configuration repository, select the blank option at the top of the dropdown and save.
Changes take effect in new sessions. Existing sessions are not affected.
## Setting up sandbox environments
The global configuration repository can also include a `.blocks/post-clone` script. This script runs automatically after Blocks clones the global configuration repository into each sandbox, making it a convenient place to set up environment configuration that should apply to every session — regardless of which repository the agent is working in.
Common uses include installing global tools, setting environment variables, or applying workspace-wide configuration.
```bash theme={null}
#!/bin/bash
set -e
# Install a global CLI tool used across all projects
curl -L https://example.com/mytool -o /usr/local/bin/mytool
chmod +x /usr/local/bin/mytool
# Configure AWS CLI for shared infrastructure access
aws configure set region us-east-1
aws configure set output json
echo "Global environment setup complete!"
```
See [Post-clone scripts](/using-blocks/features/post-cloning) for details on how to write and configure these scripts.
## When to use this
A global configuration repository is useful when:
* Multiple repositories need the same set of skills (e.g., `/review-security`, `/gen-tests`)
* You want to enforce consistent agent behavior across your workspace
* Your team maintains shared hooks or sub-agent configurations
* You need a consistent sandbox environment setup applied to every session
For repository-specific skills that only apply to one project, commit a `SKILL.md` directly to that repository instead. See [Skills](/using-blocks/features/skills) for details.
# Multi-Repo Support
Source: https://docs.blocks.team/using-blocks/features/multi-repo-support
Work across multiple repositories simultaneously with Blocks
Blocks can search, analyze, and make changes across multiple repositories in a single session. It automatically identifies relevant repositories from your workspace, the platform you're using (GitHub, GitLab, Bitbucket, Slack, Linear), and any repo names you mention explicitly.
When in doubt, being explicit is always faster:
```
@blocks search only in 'backend-api' and 'auth-service' for...
```
## Example Use Cases
### Cross-repo search
```
@blocks find all places where we're using the old authentication API across all repos
```
Blocks searches all connected repositories and returns matching files, line numbers, and links.
### Coordinated changes
```
@blocks update the user model interface in both the backend and frontend repos to add the new 'role' field
```
Blocks modifies files across repositories and opens a separate PR in each. All PRs created in the same session are linked to that session for easy tracking.
### Architecture analysis
```
@blocks explain how the user authentication flow works from the React frontend through the API gateway to the auth service
```
Blocks traces code paths across repository boundaries and explains how the services interact.
### Consistency checking
```
@blocks check if our error handling patterns are consistent between the backend and frontend repos
```
Blocks identifies inconsistencies across repos and suggests a unified approach.
## Repository Context in Different Platforms
### In GitHub, GitLab, or Bitbucket
When you mention `@blocks` in an issue or PR, Blocks has access to the current repository by default. You can reference other connected repos explicitly, and Blocks can create PRs in any accessible repo.
### In Slack
All connected repos are in scope. Best for cross-repo questions and status updates.
### In Linear
All connected repos are in scope. Blocks can create PRs across repos and reference them back in the Linear issue.
## Limitations
### Permissions
Blocks can only access repositories you've explicitly granted access to. In organization workspaces, permissions are managed at the workspace level.
### Performance
Large operations across many repos take longer. Narrow the scope with explicit repo names or more specific queries if you need faster results.
### Coordinating PRs
When Blocks creates multiple PRs for a single change, review them together before merging. Use [Plan Mode](/using-blocks/features/plan-mode) for complex multi-repo changes where you want to confirm the approach first.
## Troubleshooting
Grant Blocks access to the repository:
1. Go to Dashboard → Settings → Integrations
2. Select your provider (GitHub, GitLab, or Bitbucket)
3. Click "Configure" to add more repositories and approve permissions
Be explicit about which repos to search:
```
@blocks search only in 'backend-api' and 'auth-service' repos for...
```
Narrow the scope:
* Specify which repositories to work with
* Break large operations into smaller chunks
* Use more specific queries to reduce search space
## Next Steps
Learn the basics of using Blocks
Plan complex multi-repo changes before implementing
Manage team access to multiple repositories
# Plan Mode
Source: https://docs.blocks.team/using-blocks/features/plan-mode
## Overview
Plan Mode allows you to work with Blocks to create a detailed implementation plan before any code changes are made. This collaborative planning phase helps ensure alignment on the approach before execution begins.
## How Plan Mode Works
### Starting a Session in Plan Mode
When creating a new session, you can choose to start in Plan Mode. This is available across all Blocks integrations:
* **Dashboard**: Select "Plan Mode" when starting a new session
* **Slack**: Use `@blocks /plan ` to start a new session in Plan Mode
* **GitHub**: Use `@blocks /plan ` in an issue, PR, or review comment
* **Linear**: Use `@blocks /plan ` in an issue comment
**Example:**
```
@blocks /plan implement user authentication with OAuth
```
Plan Mode must be started with a **new session**. You cannot enter Plan Mode in a thread or conversation that has already begun.
### The Planning Process
Once in Plan Mode:
1. **Describe Your Goal**: Tell Blocks what you want to accomplish
2. **Collaborative Planning**: Blocks will create an initial implementation plan
3. **Refine the Plan**: Work with Blocks to update and improve the plan
* Ask questions about the approach
* Request changes to specific steps
* Add or remove requirements
4. **Review**: Once satisfied, review the complete plan displayed as `PLAN.md` in your session
### Implementing the Plan
When you're ready to proceed:
* Click the **"Implement this plan"** button in the session
* Blocks will switch to Edit Mode and execute the planned changes
* All context from the planning phase is preserved
## Important Limitations
**One-Way Transition**: Once a session enters Edit Mode (either by implementing a plan or starting directly in Edit Mode), you cannot return to Plan Mode in that session. Plan Mode is only available at the start of a new session.
This design ensures a clear workflow:
* **Plan Mode** → Collaborative planning and refinement
* **Edit Mode** → Implementation and code changes
If you need to create a new plan after implementation has started, begin a new session.
## When to Use Plan Mode
Plan Mode is ideal for:
* **Complex Features**: Multi-step implementations that benefit from upfront planning
* **Architectural Decisions**: When you want to discuss and align on the approach before coding
* **Learning**: Understanding how Blocks would approach a problem before changes are made
* **Collaboration**: Working with Blocks to refine requirements and implementation strategy
## When to Skip Plan Mode
You can start directly in Edit Mode for:
* **Simple Changes**: Quick fixes or straightforward modifications
* **Clear Requirements**: When the implementation approach is obvious
* **Iterative Work**: Continuing work from a previous session
* **Urgent Fixes**: When you need immediate changes
## Best Practices
* **Be Specific**: Provide clear goals and requirements during planning
* **Ask Questions**: Use Plan Mode to explore different approaches
* **Review Thoroughly**: Make sure you're satisfied with the plan before implementing
* **Iterate**: Don't hesitate to request changes to the plan multiple times
* **Start Fresh**: Begin a new session if you need to plan additional work after implementation
***
Plan Mode gives you control over the development process, ensuring you and Blocks are aligned on the approach before making any changes to your codebase.
# Post-Clone Scripts
Source: https://docs.blocks.team/using-blocks/features/post-cloning
Run setup scripts automatically after Blocks clones your repository
## Overview
Post-clone scripts run automatically after Blocks clones your repository into the sandbox, before the coding agent starts working. Use them to install dependencies, set up tooling, or run any initialization commands your project needs.
## Creating a post-clone script
1. Create a `.blocks` directory in your repository root
2. Add a file named `post-clone` or `post-clone.sh`
3. Make it executable: `chmod +x .blocks/post-clone`
```bash theme={null}
#!/bin/bash
set -e
# Install dependencies
npm ci
# Install Python packages
pip install -r requirements.txt
echo "Post-clone setup complete!"
```
The script runs with root privileges, so you can install system packages via `apt-get`.
Always start with `set -e`. Without it, the agent continues even if setup fails, which leads to confusing errors mid-task.
## Common use cases
```bash theme={null}
#!/bin/bash
set -e
npm ci
pip install -r requirements.txt
go mod download
```
```bash theme={null}
#!/bin/bash
set -e
# Install Go
GO_VERSION=1.21.0
curl -L https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz | tar -xz -C /usr/local
export PATH=$PATH:/usr/local/go/bin
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
go mod download
```
```bash theme={null}
#!/bin/bash
set -e
curl -L https://example.com/tool.tar.gz -o /tmp/tool.tar.gz
tar -xzf /tmp/tool.tar.gz -C /usr/local/bin
chmod +x /usr/local/bin/tool
tool --version
```
```bash theme={null}
#!/bin/bash
set -e
apt-get update && apt-get install -y \
python3-dev \
libpq-dev \
mysql-client
pip install -r requirements.txt
```
## Best practices
**Check before installing** — make scripts idempotent so they run cleanly every session:
```bash theme={null}
if ! command -v mytool &> /dev/null; then
curl -L https://example.com/mytool -o /usr/local/bin/mytool
chmod +x /usr/local/bin/mytool
fi
```
**Use lock files** — prefer `npm ci` over `npm install` for faster, reproducible installs.
**Add PATH exports to `~/.bashrc`** — binaries installed to custom paths need to be on PATH for the agent to find them.
**Log progress** — `echo` statements make it easy to spot where a script stalls.
## Limitations
Each Blocks session runs in a fresh sandbox. Packages installed by a post-clone script are not persisted — the script runs every time a new session starts on that repository.
For a full list of tools pre-installed in the sandbox, see [Environment](/using-blocks/sandbox/environment).
# Profiles
Source: https://docs.blocks.team/using-blocks/features/profiles
Pick a model, MCPs, and auth method on a per-invocation basis
## Overview
Profiles let you bundle an agent, model, and configuration under a single slash command. Invoke any profile with `@blocks / ...` — the same syntax used for skills and plan mode — to run a specific agent with a specific model, auth method, and MCP set.
For example, `/quick` could map to the `claude` agent on the `haiku` model with Bedrock auth and no MCP servers.
Each agent ships with an implicit default profile keyed by the agent's own keyword (e.g. `/claude`, `/codex`) that uses the agent-level settings.
## Why use profiles
* **Pin a model** for a specific kind of work (e.g. `/sonnet-latest`, `/opus`).
* **Restrict an invocation** to a subset of MCP servers (e.g. a read-only DB profile).
* **Switch auth method** (API Key vs OAuth vs AWS Bedrock) without changing the agent default.
## Creating a profile
1. Go to **Dashboard → Agents → \[Your Agent]**.
2. In the **Profiles** section click **Add Profile**.
3. Set a **Keyword** — this becomes the slash trigger. Keywords are alphanumeric, underscore, or hyphen only, and must be unique within the workspace.
4. Configure the fields described below.
5. Click **Create**.
## What you can configure
* **Keyword** — the slash trigger (`/`).
* **Authentication method** — choose between the agent's supported modes (e.g. API Key, OAuth, AWS Bedrock for Claude Code).
* **Model** — pin a specific model from the agent's available models.
* **MCP servers** — opt into a subset of MCP servers, or leave the override off to inherit the agent default.
## Using a profile
Invoke a profile the same way you invoke any other slash command:
```
@blocks /sonnet help me fix the conflict at src/app.ts
@blocks /sonnet /review-security
```
Profile keywords compose with skills and plan mode.
## Default profile
Each agent has an implicit default profile that matches the agent's own keyword (`/claude`, `/codex`, etc.) and uses the agent-level settings. The Profiles list in the dashboard labels this row as **Default**.
## Managing profiles
Edit or delete profiles from the same **Profiles** section. Changes take effect on the next invocation.
# Skills
Source: https://docs.blocks.team/using-blocks/features/skills
## Overview
Skills are slash commands that coding agents execute on demand. Invoke any skill with `/` — in the Blocks sandbox, in Slack, in GitHub, or in Linear.
Skills can be created from the Blocks dashboard or committed directly to your repos as files. All sources are merged, so the coding agent always has access to your full skill set regardless of where they're defined.
## How Blocks loads skills
Blocks discovers and merges skills from three sources:
**Dashboard skills**
Skills created in the Blocks dashboard are available workspace-wide and loaded into every coding agent session automatically.
**Global repo**
If your workspace has a global repo configured, Blocks loads skills from its `.claude/skills/`, `.codex/skills/`, and `.agents/skills/` directories. These are merged with your dashboard skills.
**Per-repo skills**
When Blocks clones a repo for a session, it scans the same directories (`.claude/skills/`, `.codex/skills/`, `.agents/skills/`) and loads any skills found there. These are immediately available to the coding agent for that session.
All three sources are merged — the coding agent sees one unified list of skills regardless of where they originated. If two sources define a skill with the same name, Blocks appends a number to avoid conflicts (e.g., `review-security` and `review-security2`).
The folder pattern (`.claude`, `.codex`, `.agents`) doesn't matter — Blocks picks up skills from all of them. Use whichever matches your team's coding agent setup.
## Creating skills
### From the dashboard
1. Navigate to **Dashboard > Skills**
2. Click **Create**
3. Enter a **Name** (this becomes the slash command, e.g., `review-security`) and a **Prompt**
4. Click **Save**
Dashboard skills are loaded into the coding agent sandbox automatically. Invoke them with `/` or select them from the Skills menu in the message box. You can add extra context after the skill name to customize the request for a specific task.
### From a repo
Create a `SKILL.md` file inside a named folder in any supported directory:
```
.claude/skills/review-security/SKILL.md
```
The folder name becomes the slash command. The `SKILL.md` file contains the prompt the coding agent will execute.
Commit skills to your **global repo** to share them across all sessions, or to any **individual repo** to make them available only when that repo is active.
**Example skill prompts:**
**review-security** — Review this code for security vulnerabilities. Focus on SQL injection, XSS, auth/authorization issues, and secrets in code. Provide specific line numbers and suggested fixes.
**add-docs** — Add documentation to this code including function/method descriptions, parameter explanations, return values, and usage examples.
**gen-tests** — Generate unit tests covering edge cases, happy paths, and error handling. Aim for at least 80% code coverage.
## Using skills
### In Slack, GitHub, Linear, and other integrations
Prefix with `@blocks`:
```
@blocks /review-security
@blocks /plan /gen-tests
@blocks /add-docs for the authentication module
```
## Managing skills
Edit or delete skills from **Dashboard > Skills**. Changes take effect in the next session.
# Getting Started
Source: https://docs.blocks.team/using-blocks/getting-started
Set up Blocks and make your first request
## Prerequisites
* Access to a GitHub, GitLab, Bitbucket, Slack, or Linear workspace
* API credentials for at least one coding agent
Start with **Claude Code** — it's the most capable agent for general tasks and requires only an [Anthropic API key](https://console.anthropic.com/).
## Step 1: Create Your Account and Connect Integrations
[Sign up at blocks.team](https://blocks.team/signup) and follow the onboarding. It will walk you through connecting your platforms and configuring your first coding agent.
You can add or update integrations at any time from **Settings → Integrations**.
Slack requires admin permissions to install. You'll also need to invite `@blocks` to each channel where you want to use it.
## Step 2: Set Your Default Agent
Only Claude Code, Codex, and OpenCode can be set as the default agent. Gemini CLI, Cursor CLI, and Sisyphus must be invoked explicitly with slash commands (`/gemini`, `/cursor`, `/sisyphus`).
To change your default: **Dashboard → Agents → Set as default agent**.
You can always override per-request with `/claude`, `/codex`, `/opencode`, or `/kimi`.
## Step 3: Make Your First Request
Mention `@blocks` in any issue, PR comment, Slack message, or Linear ticket and describe what you need in plain language. Be specific — reference issue numbers, file names, or relevant context.
Good requests:
* `@blocks fix the login timeout issue mentioned in #123`
* `@blocks review this PR for performance issues`
* `@blocks explain how the payment processing flow works`
Less effective:
* `@blocks help` (too vague)
* `@blocks fix everything` (too broad)
Blocks will react with 👀 to confirm it received your request, then reply with results and a dashboard link where you can follow progress and review logs.
## Beyond Ad-Hoc: PR Review and Automations
Once you're set up, explore the other two modes:
Automatically review every pull request with custom instructions — security checks, style guides, test coverage requirements.
Trigger agents on events: CI failures, Slack alerts, new tickets, and more.
Collaborate on an implementation plan before any code is written.
Create reusable prompts for tasks your team runs repeatedly.
## Need Help?
* Platform-specific guides: [GitHub](/using-blocks/integrations/github), [GitLab](/using-blocks/integrations/gitlab), [Bitbucket](/using-blocks/integrations/bitbucket), [Slack](/using-blocks/integrations/slack), [Linear](/using-blocks/integrations/linear)
* [Slack community](/using-blocks/support/community)
* [Contact us](/using-blocks/support/contact)
# Atlassian
Source: https://docs.blocks.team/using-blocks/integrations/atlassian
## Getting Started
Connect your Atlassian site to give Blocks access to Jira issues and Confluence pages. Once connected, Blocks can look up tickets, reference documentation, and pull sprint or project context when working in GitHub, Slack, and other platforms.
**Blocks cannot be invoked from Jira or Confluence.** Atlassian is used as a data source only — Blocks reads from your Atlassian site to provide context when handling requests made elsewhere.
## Setting Up the Atlassian Integration
Connect Atlassian via **Settings > Integrations > Atlassian** in the Blocks dashboard. OAuth connects your Atlassian site at the workspace level. Each team member can optionally link their individual Atlassian account for personalized access.
## What Blocks Can Do
Once your Atlassian site is connected, Blocks can:
* **Look Up Jira Issues**: Retrieve issue status, details, assignees, and linked tickets for relevant context
* **Reference Confluence Documentation**: Find and read architecture docs, decision records, onboarding guides, and other Confluence content
* **Pull Sprint and Project Context**: Understand what your team is working on when implementing features or answering questions
* **Provide Richer Context**: Use Atlassian content to give more accurate answers when you make requests in GitHub or Slack
## Using Atlassian in Requests
After connecting your Atlassian site, you can ask Blocks to reference your Jira or Confluence content from any supported platform:
```
@blocks implement the feature described in PROJ-123
```
```
@blocks check the Confluence architecture doc and explain how this service fits in
```
```
@blocks what issues are blocking PROJ-456?
```
```
@blocks summarize what's in the current sprint and help me prioritize
```
Blocks will automatically look up relevant Jira issues and Confluence pages when it would help complete a request. You can also explicitly reference a ticket ID or page name in your request.
## Best Practices
* **Reference Ticket IDs**: Including a Jira ticket ID (e.g. `PROJ-123`) in your request helps Blocks find the right issue quickly
* **Name Confluence Pages**: Mentioning the name of a specific Confluence page or space helps Blocks locate the right documentation
* **Keep Tickets Updated**: Blocks reads current Jira status and details — stale tickets may result in outdated context
* **Combine With Other Integrations**: Atlassian works best alongside GitHub or Slack, where Blocks can act on the context it finds
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [GitHub Integration](/using-blocks/integrations/github) for invoking Blocks from code reviews
* [Slack Integration](/using-blocks/integrations/slack) for invoking Blocks from conversations
* [Skills](/using-blocks/features/skills) for reusable prompts
# AWS
Source: https://docs.blocks.team/using-blocks/integrations/aws
## Getting Started
Connect AWS to Blocks to enable agents to interact with your cloud infrastructure. Agents can inspect resources, query services, assist with deployments, and help troubleshoot issues across your AWS environment.
Blocks operates with the full privileges of the connected IAM user. We strongly recommend provisioning a dedicated IAM user with read-only access (e.g. the `ReadOnlyAccess` managed policy) to operate safely.
## What Blocks Can Do
Blocks can help you with a variety of AWS tasks:
* **Inspect Resources**: Query EC2 instances, S3 buckets, RDS databases, Lambda functions, and more
* **Monitor Logs**: Retrieve and analyze CloudWatch logs to diagnose issues
* **Deployment Assistance**: Help plan and execute deployments via ECS, Lambda, or CloudFormation
* **Cost Analysis**: Review resource usage and suggest cost optimizations
* **Security Review**: Audit IAM roles, security groups, and access policies
* **Infrastructure Troubleshooting**: Diagnose connectivity, configuration, and performance issues
## Setting Up AWS Integration
To connect AWS to Blocks:
In the [AWS IAM Console](https://console.aws.amazon.com/iam/), create a new IAM user dedicated to Blocks.
We strongly recommend creating this user with **minimal read-only permissions** (e.g. the `ReadOnlyAccess` managed policy). Never use root account credentials or credentials with unrestricted access.
Create an access key for the IAM user and copy the **Access Key ID** and **Secret Access Key**.
In the Blocks dashboard, go to **Settings > AWS** and enter:
* **AWS Access Key ID**
* **AWS Secret Access Key**
**AWS OIDC support coming soon** — We're working on federated identity support so you can connect without long-lived access keys.
### Using AWS in Requests
Once connected, you can reference your AWS environment in any request:
```
@blocks list all running EC2 instances in us-east-1
```
```
@blocks check CloudWatch logs for the payment-service Lambda in the last hour
```
```
@blocks review the security groups attached to our production RDS instance
```
```
@blocks what's causing high memory usage on the web-server ECS task?
```
**Session Context**: Responding in the same thread continues the session, preserving context from previous queries. Starting a new comment or issue creates a fresh session.
## Security Best Practices
* **Follow Least Privilege**: Grant only the permissions Blocks needs for your specific use cases
* **Rotate Credentials**: Regularly rotate access keys and update them in Settings > AWS
* **Restrict by Region**: Scope IAM policies to specific regions where possible
* **Monitor Activity**: Enable CloudTrail to audit all API calls made by Blocks in your account
* **Use Separate Accounts**: Consider using a dedicated AWS account for staging vs. production
## Common Use Cases
### Infrastructure Debugging
Ask Blocks to investigate issues in your environment:
```
@blocks the checkout service is returning 502 errors, check the ECS task logs and load balancer health
```
### Deployment Review
Get help reviewing before applying changes:
```
@blocks review this CloudFormation template for any security or cost concerns before we deploy
```
### Cost Optimization
Identify opportunities to reduce spend:
```
@blocks find underutilized EC2 instances and RDS databases in our staging environment
```
## Best Practices
* **Be Specific**: Reference service names, regions, and resource IDs where possible
* **Provide Context**: Describe the expected behavior alongside what you're observing
* **Check Progress**: Use the dashboard link to monitor Blocks' work on your request
* **Follow Up**: Continue the conversation by mentioning `@blocks` with additional questions or clarifications
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [Plan Mode](/using-blocks/features/plan-mode) for collaborative planning before making infrastructure changes
* [Skills](/using-blocks/features/skills) for reusable AWS queries and workflows
# Bitbucket
Source: https://docs.blocks.team/using-blocks/integrations/bitbucket
## Getting Started
Interact with Blocks on Bitbucket by mentioning `@blocks` in any issue or pull request comment with your request. Manage issues, answer questions, review PRs, implement changes, and more.
Connect Bitbucket via **Settings > Integrations > Bitbucket** in the Blocks dashboard. OAuth connects to a Bitbucket workspace. Each team member must also link their individual Bitbucket account.
Delegate your request to a specific agent by mentioning the agent keyword in a slash command. Example: /claude, /codex, /gemini, /kimi. If none are mentioned, the request will default to your default agent. To change it: Dashboard → Agents → Set as default agent.
## Bot Identity Setup
Blocks will respond on Bitbucket using the identity of whoever authorized the integration. To give Blocks a distinct identity — so responses appear from a dedicated bot account rather than a personal user — we recommend creating a separate Bitbucket user for this purpose before completing the integration.
**Steps:**
1. Create a new Bitbucket user account for your bot identity (e.g. `Blocks Bot`)
2. Log into that Bitbucket account
3. Create a new Blocks workspace user using that account's email
4. Invite the new Blocks user to your workspace
5. While logged in as the bot's Blocks account, go to **Settings > Connected Accounts** and connect the Bitbucket account
6. Complete the Bitbucket integration via **Settings > Integrations > Bitbucket** while authenticated as the bot Blocks user
Once setup is complete, you can log back into your normal Bitbucket and Blocks accounts for everyday use. This is a one-time setup.
## What Blocks Can Do
Blocks can help you with a variety of tasks on Bitbucket:
* **Answer Questions**: Ask Blocks about an issue or PR
* **Update Issues**: Request changes to an issue such as additional details with context from your codebase
* **Create New Issues**: Ask Blocks to create additional tickets based on your requirements
* **Implementation**: Ask Blocks to create a PR from an issue
* **PR Review**: Ask Blocks to review a pull request. Optionally, include context for what to look out for
* **Make Changes**: Ask Blocks to make changes to an open PR
### Making a Request
Simply mention `@blocks` in any Bitbucket issue or PR comment followed by your request:
```
@blocks what does this function do?
```
```
@blocks can you implement this issue?
```
```
@blocks review this PR, focus on any potential runtime errors
```
```
@blocks can you change this to a class instead?
```
**Session Context**: Responding in a Bitbucket pull request thread will continue in the same session, preserving all context from the conversation. Creating a new comment on an issue or PR will create a fresh session with no prior context.
### Plan Mode
Start a new session in Plan Mode to create an implementation plan before making changes:
```
@blocks /plan implement user authentication with OAuth
```
Plan Mode allows you to collaborate on the approach before any code is written. Learn more about [Plan Mode](/using-blocks/features/plan-mode).
Plan Mode must be started with a new session and cannot be entered once implementation has begun.
### Blocks Response Process
1. **Acknowledgment**: Blocks will add 👀 (eye) emojis to indicate it has seen your message
2. **Dashboard Link**: Blocks responds with a link to the dashboard where you can track progress
3. **Progress Updates**: Blocks provides a realtime snippet about what it is currently working on
4. **Final Message**: Blocks responds with a final message which summarizes what it did
## Best Practices
* **Be Specific**: Provide clear and detailed requests to get the best results
* **Use Context**: Reference specific issues, sections of the code, or requirements when relevant
* **Check Progress**: Use the dashboard link to monitor Blocks' work on your request
* **Follow Up**: You can continue the conversation by mentioning `@blocks` again with additional questions or clarifications
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [Plan Mode](/using-blocks/features/plan-mode) for collaborative planning
* [Skills](/using-blocks/features/skills) for reusable prompts
* [Multi-Repo Support](/using-blocks/features/multi-repo-support) for working across repositories
# GitHub
Source: https://docs.blocks.team/using-blocks/integrations/github
## Getting Started
Interact with Blocks on GitHub by mentioning `@blocks` in any issue, PR, or PR review comment with your request. Manage issues, answer questions, review PRs, make changes, create tickets, and more.
Delegate your request to a specific agent by mentioning the agent keyword in a slash command. Example: /claude, /codex, /gemini, /kimi. If none are mentioned, the request will default to your default agent. To change it: Dashboard → Agents → Set as default agent.
## What Blocks Can Do
Blocks can help you with a variety of tasks on GitHub:
* **Answer Questions**: Ask Blocks about an issue or PR
* **Update Issues**: Request changes to an issue such as additional details with context from your codebase
* **Create New Issues**: Ask Blocks to create additional tickets based on your requirements
* **Implementation**: Ask Blocks to create a PR from an issue
* **PR Review**: Ask Blocks to review a PR. Optionally, include context for what to look out for
* **Make Changes**: Ask Blocks to make changes to an open PR
### Making a Request
Simply mention `@blocks` in any GitHub issue, PR, or PR review comment followed by your request:
```
@blocks what does this function do?
```
```
@blocks can you implement this issue?
```
```
@blocks review this PR, focus on any potential runtime errors
```
```
@blocks can you change this be a class instead?
```
**Session Context**: Responding in a GitHub pull request review thread will continue in the same session, preserving all context from the conversation. Creating a new comment on an issue, PR, or PR file will create a fresh session with no prior context.
### Plan Mode
Start a new session in Plan Mode to create an implementation plan before making changes:
```
@blocks /plan implement user authentication with OAuth
```
Plan Mode allows you to collaborate on the approach before any code is written. Learn more about [Plan Mode](/using-blocks/features/plan-mode).
Plan Mode must be started with a new session and cannot be entered once implementation has begun.
### Blocks Response Process
1. **Acknowledgment**: Blocks will add 👀 (eye) emojis to indicate it has seen your message
2. **Dashboard Link**: Blocks responds with a link to the dashboard where you can track progress
3. **Progress Updates**: Blocks provides a realtime snippet about what it is currently working on
4. **Final Message**: Blocks responds with a final message which summarizes what it did
## Bot Invocations
By default, Blocks ignores mentions from GitHub bot users. If you want to allow bots to invoke Blocks and create sessions, enable **Bot Invocations** in **Dashboard → Settings → GitHub**.
## Best Practices
* **Be Specific**: Provide clear and detailed requests to get the best results
* **Use Context**: Reference specific issues, sections of the code, or requirements when relevant
* **Check Progress**: Use the dashboard link to monitor Blocks' work on your request
* **Follow Up**: You can continue the conversation by mentioning `@blocks` again with additional questions or clarifications
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [Plan Mode](/using-blocks/features/plan-mode) for collaborative planning
* [Skills](/using-blocks/features/skills) for reusable prompts
* [Multi-Repo Support](/using-blocks/features/multi-repo-support) for working across repositories
# GitHub Enterprise (MU)
Source: https://docs.blocks.team/using-blocks/integrations/github-enterprise-mu
Connect a GitHub Enterprise (MU) managed user tenant to Blocks with a customer-owned GitHub App
## Overview
GitHub Enterprise (MU) connects Blocks to a GitHub Enterprise managed user tenant by using a GitHub App that you create and own in your enterprise organization.
This guide uses `CubeOrgSquaredApp` as an example app name. You can choose any unique, descriptive name for your own app.
This setup creates fresh credentials for your GitHub App. Do not copy app IDs, client IDs, private keys, webhook secrets, or client secrets from another app.
## Prerequisites
* Organization owner or admin access in your GitHub Enterprise managed user tenant
* Admin access to your Blocks workspace
* Your enterprise web host URL, such as `https://cubeorg.ghe.com`
* Your enterprise API host URL, such as `https://api.cubeorg.ghe.com`
* A secure place to store the generated webhook secret, client secret, and private key
For GitHub's own setup references, see [Registering a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app), [Choosing permissions for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app), and [Managing private keys for GitHub Apps](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps).
## Step 1: Create a GitHub App
In your enterprise organization, open:
```txt theme={null}
https:///organizations//settings/apps/new
```
For example, if your enterprise web host is `https://cubeorg.ghe.com` and your org is `CubeOrgSquared`, open:
```txt theme={null}
https://cubeorg.ghe.com/organizations/CubeOrgSquared/settings/apps/new
```
GitHub also links to this flow from **Organization settings > Developer settings > GitHub Apps > New GitHub App**.
## Step 2: Enter basic app details
Use these values when creating the app:
| Field | Value |
| --------------- | ------------------------------------------ |
| GitHub App name | A unique name, such as `CubeOrgSquaredApp` |
| Description | Optional |
| Homepage URL | `https://blocks.team` |
Under **Identifying and authorizing users**, add these callback URLs in this exact order:
```txt theme={null}
https://api.prod.blocks.team/v1/webhooks/github/completion
https://api.prod.blocks.team/v1/webhooks/github/redirect
https://api.prod.blocks.team/v1/webhooks/github/redirect?is_onboarding=true
```
Then set:
| Option | Setting |
| ------------------------------------------------------ | --------- |
| Request user authorization (OAuth) during installation | Checked |
| Enable Device Flow | Unchecked |
Under **Post installation**, leave the setup URL empty and leave **Redirect on update** unchecked.
## Step 3: Configure the webhook
Under **Webhook**, set:
| Field | Value |
| ---------------- | -------------------------------------------------------- |
| Active | Checked |
| Webhook URL | `https://api.prod.blocks.team/v1/webhooks/github/events` |
| Secret | Generate a new strong secret and save it securely |
| SSL verification | Enable SSL verification |
GitHub documents the events available to GitHub Apps in [Webhook events and payloads](https://docs.github.com/en/webhooks/webhook-events-and-payloads).
## Step 4: Set repository permissions
Under **Repository permissions**, set only the permissions Blocks needs:
| Permission | Access |
| -------------- | ------------ |
| Actions | Read-only |
| Administration | Read & write |
| Checks | Read & write |
| Contents | Read & write |
| Deployments | Read & write |
| Discussions | Read & write |
| Environments | Read & write |
| Issues | Read & write |
| Metadata | Read-only |
| Pull requests | Read & write |
| Webhooks | Read & write |
| Workflows | Read & write |
Leave all other repository permissions set to **No access**.
## Step 5: Set organization permissions
Under **Organization permissions**, set:
| Permission | Access |
| ------------------------------ | ------------ |
| Administration | Read & write |
| Members | Read-only |
| Personal access token requests | Read & write |
| Webhooks | Read-only |
Leave all other organization permissions set to **No access**.
Under **Account permissions**, leave every permission set to **No access**. Under **Enterprise permissions**, do not select any permissions.
## Step 6: Subscribe to events
Subscribe to exactly these events:
* Commit comment
* Create
* Delete
* Issue comment
* Issues
* Label
* Pull request
* Pull request review
* Pull request review comment
* Pull request review thread
* Push
* Repository
* Sub issues
* Workflow dispatch
* Workflow run
Leave all other events unchecked.
## Step 7: Choose app visibility
Under **Where can this GitHub App be installed?**, choose the option that matches your enterprise policy:
* **Only on this account** if the app should only be installed in the organization where you created it
* **Any account** if your enterprise admins want the same app to be installable by other accounts on the tenant
If you are recreating an existing app, match the original app's visibility and any optional feature settings before finishing.
## Step 8: Generate app credentials
After you create the GitHub App:
1. Copy the new **App ID**.
2. Copy the new **Client ID**.
3. Generate a new **client secret** and save it securely.
4. Generate a new **private key**, download the `.pem` file, and save it securely.
5. Keep the webhook secret you generated in Step 3 available for the Blocks configuration form.
Private keys, client secrets, and webhook secrets are sensitive credentials. Store them in your normal secrets manager and avoid pasting them into tickets, chat messages, or documentation.
## Step 9: Select the GitHub Enterprise (MU) integration
In Blocks, go to **Settings > Integrations > GitHub**. In the GitHub organization row, choose **GitHub Enterprise (MU)** from the dropdown, then click **Configure**.
## Step 10: Configure Blocks
Fill in the values from your GitHub App in the configuration dialog.
| Blocks field | Value |
| --------------- | --------------------------------------------------------------- |
| Web host URL | Your enterprise web host, such as `https://cubeorg.ghe.com` |
| API host URL | Your enterprise API host, such as `https://api.cubeorg.ghe.com` |
| GitHub App ID | The App ID from the GitHub App settings page |
| GitHub App name | The app slug/name you created, such as `CubeOrgSquaredApp` |
| Client ID | The Client ID from the GitHub App settings page |
| Client secret | The client secret you generated after app creation |
| Private key | The full contents of the downloaded `.pem` private key |
| Webhook secret | The webhook secret you created in Step 3 |
Click **Configure** to save the integration.
## Step 11: Install the app
Install the GitHub App on the organization and repositories Blocks should access. You can install it on all repositories or choose a selected set, depending on your team's policy.
After installation, you can mention `@blocks` in issues, pull requests, and pull request review comments in connected repositories.
## Troubleshooting
* **OAuth redirect fails**: Confirm the callback URLs are present and ordered exactly as shown in Step 2.
* **Webhook deliveries fail**: Confirm the webhook URL, webhook secret, and SSL verification setting match Step 3.
* **Blocks cannot read or write repository data**: Recheck the repository and organization permissions, then reinstall or update the GitHub App installation.
* **Private key errors**: Generate a fresh private key in GitHub, update the Blocks configuration, and make sure the pasted value includes the full `BEGIN` and `END` lines.
# GitLab
Source: https://docs.blocks.team/using-blocks/integrations/gitlab
## Getting Started
Interact with Blocks on GitLab by mentioning `@blocks` in any issue or merge request comment with your request. Manage issues, answer questions, review MRs, implement changes, and more.
Connect GitLab via **Settings > Integrations > GitLab** in the Blocks dashboard. OAuth connects to a top-level GitLab group or your personal namespace. Subgroups are not supported — only top-level groups can be connected. Each team member must also link their individual GitLab account.
Delegate your request to a specific agent by mentioning the agent keyword in a slash command. Example: /claude, /codex, /gemini, /kimi. If none are mentioned, the request will default to your default agent. To change it: Dashboard → Agents → Set as default agent.
## Bot Identity Setup
Blocks will respond on GitLab using the identity of whoever authorized the integration. To give Blocks a distinct identity — so responses appear from a dedicated bot account rather than a personal user — we recommend creating a separate GitLab user for this purpose before completing the integration.
**Steps:**
1. Create a new GitLab user account for your bot identity (e.g. `Blocks Bot`)
2. Log into that GitLab account
3. Create a new Blocks workspace user using that account's email
4. Invite the new Blocks user to your workspace
5. While logged in as the bot's Blocks account, go to **Settings > Connected Accounts** and connect the GitLab account
6. Complete the GitLab integration via **Settings > Integrations > GitLab** while authenticated as the bot Blocks user
Once setup is complete, you can log back into your normal GitLab and Blocks accounts for everyday use. This is a one-time setup.
## What Blocks Can Do
Blocks can help you with a variety of tasks on GitLab:
* **Answer Questions**: Ask Blocks about an issue or MR
* **Update Issues**: Request changes to an issue such as additional details with context from your codebase
* **Create New Issues**: Ask Blocks to create additional tickets based on your requirements
* **Implementation**: Ask Blocks to create an MR from an issue
* **MR Review**: Ask Blocks to review a merge request. Optionally, include context for what to look out for
* **Make Changes**: Ask Blocks to make changes to an open MR
### Making a Request
Simply mention `@blocks` in any GitLab issue or MR comment followed by your request:
```
@blocks what does this function do?
```
```
@blocks can you implement this issue?
```
```
@blocks review this MR, focus on any potential runtime errors
```
```
@blocks can you change this to a class instead?
```
**Session Context**: Responding in a GitLab merge request thread will continue in the same session, preserving all context from the conversation. Creating a new comment on an issue or MR will create a fresh session with no prior context.
### Plan Mode
Start a new session in Plan Mode to create an implementation plan before making changes:
```
@blocks /plan implement user authentication with OAuth
```
Plan Mode allows you to collaborate on the approach before any code is written. Learn more about [Plan Mode](/using-blocks/features/plan-mode).
Plan Mode must be started with a new session and cannot be entered once implementation has begun.
### Blocks Response Process
1. **Acknowledgment**: Blocks will add 👀 (eye) emojis to indicate it has seen your message
2. **Dashboard Link**: Blocks responds with a link to the dashboard where you can track progress
3. **Progress Updates**: Blocks provides a realtime snippet about what it is currently working on
4. **Final Message**: Blocks responds with a final message which summarizes what it did
## Best Practices
* **Be Specific**: Provide clear and detailed requests to get the best results
* **Use Context**: Reference specific issues, sections of the code, or requirements when relevant
* **Check Progress**: Use the dashboard link to monitor Blocks' work on your request
* **Follow Up**: You can continue the conversation by mentioning `@blocks` again with additional questions or clarifications
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [Plan Mode](/using-blocks/features/plan-mode) for collaborative planning
* [Skills](/using-blocks/features/skills) for reusable prompts
* [Multi-Repo Support](/using-blocks/features/multi-repo-support) for working across repositories
# Linear
Source: https://docs.blocks.team/using-blocks/integrations/linear
## Getting Started
Interact with Blocks on Linear by mentioning `@blocks` in any issue comment with your request. Manage issues, answer questions, improve tickets with technical details from your codebase, delegate implementations, and more.
Delegate your request to a specific agent by mentioning the agent keyword in a slash command. Example: /claude, /codex, /gemini, /kimi. If none are mentioned, the request will default to your default agent. To change it: Dashboard → Agents → Set as default agent.
## What Blocks Can Do
Blocks can help you with a variety of tasks on Linear:
* **Answer Questions**: Ask Blocks about project status, issue details, or technical questions
* **Update Tickets**: Request changes to an issue such as additional details with context from your codebase
* **Create New Issues**: Ask Blocks to create additional tickets based on your requirements
* **Start Implementation**: Request Blocks to begin working on the current issue you're commenting on
Blocks can work across many repos in a single request
### Making a Request
Simply mention `@blocks` in any Linear issue comment followed by your request:
```
@blocks create a new issue to add unit tests for this
```
```
@blocks implement this ticket, create a pr
```
```
@blocks add code examples from the relevant repository to this ticket
```
**Session Context**: Responding in a Linear comment thread will continue in the same session, preserving all context from the conversation. Starting a new comment thread on an issue will create a fresh session with no prior context.
### Plan Mode
Start a new session in Plan Mode to create an implementation plan before making changes:
```
@blocks /plan implement this feature with a focus on scalability
```
Plan Mode allows you to collaborate on the approach before any code is written. Learn more about [Plan Mode](/using-blocks/features/plan-mode).
Plan Mode must be started with a new session and cannot be entered once implementation has begun.
### Blocks Response Process
1. **Acknowledgment**: Blocks will add 👀 (eye) emojis to indicate it has seen your message
2. **Dashboard Link**: Blocks responds with a link to the dashboard where you can track progress
3. **Progress Updates**: Blocks provides a realtime snippet about what it is currently working on
4. **Final Message**: Blocks responds with a final message which summarizes what it did
## Setting a Default Linear Team
Configure a default Linear team to streamline issue creation. When set, Blocks will automatically use your default team when creating new Linear issues unless you specify a different team in your request.
**Setting your default team:**
1. Navigate to Settings > Integrations in the Blocks dashboard
2. Navigate to Linear > Configure in the integrations page
3. Select your preferred team from the dropdown
4. Your selection is saved automatically
Once configured, all new issues created by Blocks will be assigned to your default team, making it easier to organize and track work across your organization.
## Best Practices
* **Be Specific**: Provide clear and detailed requests to get the best results
* **Use Context**: Reference specific issues, PRs, or requirements when relevant
* **Check Progress**: Use the dashboard link to monitor Blocks' work on your request
* **Follow Up**: You can continue the conversation by mentioning `@blocks` again with additional questions or clarifications
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [Plan Mode](/using-blocks/features/plan-mode) for collaborative planning
* [Skills](/using-blocks/features/skills) for reusable prompts
* [Multi-Repo Support](/using-blocks/features/multi-repo-support) for working across repositories
# Notion
Source: https://docs.blocks.team/using-blocks/integrations/notion
## Getting Started
Connect Notion to give Blocks access to your workspace pages and databases. Once connected, Blocks can look up documentation, reference specs, and pull context from Notion when working in GitHub, Slack, Linear, and other platforms.
**Blocks cannot be invoked from Notion.** Notion is used as a data source only — Blocks reads from your Notion workspace to provide context when handling requests made elsewhere.
## Setting Up the Notion Integration
Connect Notion via **Settings > Integrations > Notion** in the Blocks dashboard. OAuth connects your Notion workspace and links your user account.
## What Blocks Can Do
Once your Notion workspace is connected, Blocks can:
* **Look Up Documentation**: Find and reference internal docs, runbooks, and guides stored in Notion
* **Reference Specs and Requirements**: Pull product requirements, design specs, and technical designs into context when implementing features
* **Search Pages and Databases**: Search across your workspace to find relevant information for a given task
* **Provide Richer Context**: Use Notion content to give more accurate, context-aware answers when you ask questions in GitHub or Slack
## Using Notion in Requests
After connecting Notion, you can ask Blocks to reference your workspace content from any supported platform:
```
@blocks implement this feature according to the spec in our Notion docs
```
```
@blocks check our Notion runbook and help me debug this issue
```
```
@blocks summarize the requirements for this project based on our Notion pages
```
Blocks will automatically search your Notion workspace for relevant content when it would help complete a request. You can also explicitly ask Blocks to look something up in Notion.
## Best Practices
* **Organize Your Workspace**: Well-structured Notion pages with clear titles make it easier for Blocks to find relevant content
* **Be Specific**: Reference specific page names or topics when asking Blocks to look something up
* **Keep Docs Updated**: Blocks reads your current Notion content — outdated pages may lead to outdated answers
* **Combine With Other Integrations**: Notion works best alongside GitHub, Slack, or Linear, where Blocks can act on the context it finds
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [GitHub Integration](/using-blocks/integrations/github) for invoking Blocks from code reviews
* [Slack Integration](/using-blocks/integrations/slack) for invoking Blocks from conversations
* [Skills](/using-blocks/features/skills) for reusable prompts
# Postgres
Source: https://docs.blocks.team/using-blocks/integrations/postgres
## Getting Started
Connect Postgres databases to Blocks to enable agents to query, analyze, and work with your database schema and data. Agents can read schema information, execute queries, and help with database-related tasks.
Blocks supports read-only access to Postgres databases by default. Write access can be enabled but requires explicit configuration.
## What Blocks Can Do
Blocks can help you with a variety of database tasks:
* **Schema Analysis**: Examine table structures, relationships, and constraints
* **Query Execution**: Run SELECT queries to retrieve data for analysis
* **Data Analysis**: Analyze trends, generate reports, and answer data-related questions
* **Migration Planning**: Review schema changes and suggest migration strategies
* **Query Optimization**: Analyze slow queries and suggest improvements
## Setting Up Postgres Integration
To connect a Postgres database to Blocks:
Go to Settings > Database in the Blocks dashboard
Enter your Postgres connection string in the format:
```
postgresql://user:password@host:5432/database
```
Replace the values with your actual database credentials:
* **user**: Database username
* **password**: Database password
* **host**: Your Postgres server hostname or IP address
* **5432**: Database port (default is 5432)
* **database**: Database name
If your database is behind a bastion host or requires SSH tunneling:
1. Enable the "Connect with Bastion" toggle
2. Provide the following SSH details:
* **SSH Private Key**: Your private key for SSH authentication (required)
* **SSH Host**: Bastion host address (required)
* **SSH User**: SSH username (required, typically "ubuntu" or "ec2-user")
* **SSH Password**: SSH password if using password authentication (optional)
Click "Save Changes" to store your database configuration securely
Always use dedicated read-only database users with minimal permissions for Blocks integrations. Never use admin or superuser credentials.
### SSH Tunnel / Bastion Host Support
If your Postgres database is not publicly accessible and requires SSH tunneling through a bastion host, Blocks supports this configuration. When you enable "Connect with Bastion", Blocks will establish an SSH tunnel to your bastion host before connecting to your database.
This is useful when:
* Your database is in a private VPC or subnet
* You require an additional layer of security
* Your infrastructure uses a jump server or bastion host pattern
When using bastion connections, ensure your SSH private key has the appropriate permissions and your bastion host is configured to allow connections to your database.
### Using Postgres in Requests
Once connected, you can reference your database in requests:
```
@blocks analyze the users table schema and explain the relationships
```
```
@blocks query the orders table for revenue trends over the last 30 days
```
```
@blocks check the slow query log and suggest optimization for the top 5 slowest queries
```
**Session Context**: All database queries are executed within the security context of the configured user. Blocks cannot access data or tables that the user doesn't have permissions for.
## Security Best Practices
* **Use Read-Only Users**: Create dedicated database users with SELECT-only permissions
* **Limit Table Access**: Grant access only to tables that agents need to query
* **Enable SSL**: Use `sslmode=require` in your connection string for encrypted connections:
```
postgresql://user:password@host:5432/database?sslmode=require
```
* **Rotate Credentials**: Regularly update database passwords and connection strings in Settings > Database
* **Monitor Queries**: Review query logs to ensure agents are only accessing appropriate data
* **Network Security**: Use bastion hosts, SSH tunneling, IP allowlisting, or VPN access for database connections
* **Secure Storage**: All connection strings and SSH credentials are stored securely as sensitive environment variables
## Common Use Cases
### Database Documentation
Ask Blocks to generate documentation for your database schema:
```
@blocks create documentation for all tables in the public schema, including column descriptions and relationships
```
### Data Investigation
Use Blocks to investigate data issues or anomalies:
```
@blocks find all orders with null customer_id and explain why this might be happening
```
### Schema Migrations
Get help planning database changes:
```
@blocks review this migration plan and suggest any missing indexes or constraints
```
## Best Practices
* **Be Specific**: Clearly specify which tables or schemas you want to work with
* **Provide Context**: Include relevant business logic or constraints in your requests
* **Review Queries**: Always review generated queries before executing them in production
* **Use Appropriate Access**: Configure different database connections for dev, staging, and production environments
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [Skills](/using-blocks/features/skills) for reusable database queries
* [Security Best Practices](/using-blocks/security) for protecting your data
# Sentry
Source: https://docs.blocks.team/using-blocks/integrations/sentry
## Getting Started
Connect your Sentry organization to give Blocks access to error reports and issue data. Once connected, Blocks can look up errors, read stack traces, and pull error context when working in GitHub, Slack, and other platforms.
**Blocks cannot be invoked from Sentry.** Sentry is used as a data source only — Blocks reads from your Sentry organization to provide context when handling requests made elsewhere.
## Setting Up the Sentry Integration
Connect Sentry via **Settings > Integrations > Sentry** in the Blocks dashboard. A single OAuth flow connects your Sentry organization — no per-user account linking is required.
## What Blocks Can Do
Once your Sentry organization is connected, Blocks can:
* **Look Up Error Reports**: Find and read Sentry issues including error messages, frequency, and affected users
* **Read Stack Traces**: Inspect stack traces and exception details to understand the root cause of errors
* **Reference Open Issues**: Pull relevant Sentry issues into context when debugging or implementing fixes
* **Provide Richer Context**: Use Sentry error data to give more accurate, targeted answers when you make requests in GitHub or Slack
## Using Sentry in Requests
After connecting your Sentry organization, you can ask Blocks to reference error data from any supported platform:
```
@blocks investigate the TypeError in the checkout flow that Sentry has been reporting
```
```
@blocks fix the error in Sentry issue PROJ-1234
```
```
@blocks look at the recent Sentry errors in the payments service and suggest what might be causing them
```
```
@blocks implement a fix for the null pointer exception showing up in Sentry this week
```
Blocks will automatically search your Sentry organization for relevant issues when it would help complete a request. You can also explicitly reference a Sentry issue ID or describe the error you want Blocks to look up.
## Best Practices
* **Reference Issue IDs**: Including a Sentry issue ID in your request helps Blocks find the exact error quickly
* **Describe the Error**: If you don't have an issue ID, describing the error message or affected area helps Blocks locate relevant issues
* **Combine With GitHub**: Sentry works best alongside GitHub, where Blocks can investigate an error and immediately open a PR with a fix
* **Keep Environments Organized**: If you have multiple Sentry projects or environments, mention which one is relevant when making a request
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [GitHub Integration](/using-blocks/integrations/github) for invoking Blocks from code reviews
* [Slack Integration](/using-blocks/integrations/slack) for invoking Blocks from conversations
* [Skills](/using-blocks/features/skills) for reusable prompts
# Slack
Source: https://docs.blocks.team/using-blocks/integrations/slack
## Getting Started
Interact with Blocks on Slack by mentioning `@blocks` in any channel with your request. Answer questions, create issues from threads, search channel history, delegate technical work, and more.
Blocks cannot be mentioned in external connection channels or in DMs between Slack users.
### Add Blocks to a channel
Blocks can only respond in or access channels that it is a member of. You can also message the Blocks app directly. To add Blocks to a channel, simply mention `@blocks` in the desired channel.
Delegate your request to a specific agent by mentioning the agent keyword in a slash command. Example: /claude, /codex, /gemini, /kimi. If none are mentioned, the request will default to your default agent. To change it: Dashboard → Agents → Set as default agent.
## What Blocks Can Do
Blocks can help you with a variety of tasks on Slack:
* **Answer Questions**: Ask Blocks about a thread, technical details, search for information, and more
* **Delegate Work**: Ask Blocks to begin implementation or to create a ticket from a thread
* **Status Updates**: Ask Blocks about the status of a feature, summaries of recent commits, open tickets, and more
Blocks can work across many repos in a single request
### Making a Request
Simply mention `@blocks` in any Slack channel where Blocks is a member followed by your request:
```
@blocks provide me a github diff url for the changes since last deployment
```
```
@blocks can you create a linear ticket based on this convo?
```
```
@blocks summarize all the commits over the last 3 days across all repos, provide links
```
**Session Context**: Responding in a Slack message thread will continue in the same session, preserving all context from the conversation. Mentioning Blocks outside of a thread will create a fresh session with no prior context.
### Plan Mode
Start a new session in Plan Mode to create an implementation plan before making changes:
```
@blocks /plan add caching layer to the API
```
Plan Mode allows you to collaborate on the approach before any code is written. Learn more about [Plan Mode](/using-blocks/features/plan-mode).
Plan Mode must be started with a new session and cannot be entered once implementation has begun.
### Messaging Blocks Privately
You can have a private, one-on-one conversation with Blocks directly in Slack using the Agent feature.
1. In the top right of Slack, click **Add an Agent** and select **Blocks**
2. Blocks will appear in your top right — click the **Blocks** button to open a private conversation
3. Start chatting directly with Blocks
Each new conversation you start creates a fresh Blocks session. Follow-up messages within the same conversation are part of the same session, preserving all prior context.
### Blocks Response Process
1. **Acknowledgment**: Blocks will add 👀 (eye) emojis to indicate it has seen your message
2. **Dashboard Link**: Blocks responds with a link to the dashboard where you can track progress
3. **Progress Updates**: Blocks provides a realtime snippet about what it is currently working on
4. **Final Message**: Blocks responds with a final message which summarizes what it did
## Best Practices
* **Be Specific**: Provide clear and detailed requests to get the best results
* **Use Context**: Reference specific issues, PRs, or requirements when relevant
* **Check Progress**: Use the dashboard link to monitor Blocks' work on your request
* **Follow Up**: You can continue the conversation by mentioning `@blocks` again with additional questions or clarifications
## Next Steps
New to Blocks? Check out the [Getting Started guide](/using-blocks/getting-started) to learn the basics.
Learn more about:
* [Plan Mode](/using-blocks/features/plan-mode) for collaborative planning
* [Skills](/using-blocks/features/skills) for reusable prompts
* [Multi-Repo Support](/using-blocks/features/multi-repo-support) for working across repositories
# Ad-Hoc
Source: https://docs.blocks.team/using-blocks/products/ad-hoc
Mention @blocks anywhere to get immediate help from a coding agent
Ad-hoc is the most direct way to use Blocks. Mention `@blocks` in a comment or message, describe what you need, and a coding agent responds — no setup beyond connecting your platform.
## Supported platforms
* GitHub — issues and pull requests
* GitLab — issues and merge requests
* Bitbucket — issues and pull requests
* Slack — any channel where `@blocks` is present
* Linear — issue comments
## How to use it
Mention `@blocks` followed by a plain language description. Be specific — reference issue numbers, file names, or relevant context.
Blocks will react with 👀 to confirm it received your request, then respond with results and a dashboard link to follow progress and review logs.
**Examples:**
* `@blocks fix the login timeout issue mentioned in #123`
* `@blocks review this PR for security vulnerabilities`
* `@blocks explain how the payment processing flow works across the repos`
* `@blocks implement the user profile page as described in this ticket`
## Choosing an agent
By default, Blocks uses whichever agent you've set as your default. To use a different agent for a specific request, prefix with a slash command:
`/claude`, `/codex`, `/opencode`, `/kimi`, `/gemini`, `/cursor`, `/sisyphus`
For example: `@Blocks /claude review this PR for performance issues`
See the [Agents](/using-blocks/agents/claude-code) section for guidance on which agent to pick.
## Plan mode
For larger changes, Blocks can propose an implementation plan before writing any code. Use the `/plan` slash command:
`@blocks /plan refactor the auth middleware to use the new token format`
Blocks will describe its approach and wait for your approval before proceeding. See [Plan Mode](/using-blocks/features/plan-mode) for details.
# Automations
Source: https://docs.blocks.team/using-blocks/products/automations
Trigger coding agents automatically on events — no mention required
Automations run a coding agent whenever a specified event occurs. Define a trigger, write a prompt, and Blocks handles the rest — no manual mention needed.
## Setup
1. Go to **Settings → Automations → Create Automation**
2. Give it a name and set visibility (workspace-wide or private)
3. Select an agent
4. Choose one or more trigger events
5. Write a prompt — this is what the agent receives when the trigger fires
6. Save
## Available triggers
| Trigger | Description |
| ------------------------------- | ------------------------------------- |
| GitHub Pull Request | Fires when a PR is opened or updated |
| GitHub Actions Workflow Failure | Fires when a GitHub Actions run fails |
| GitLab Merge Request | Fires when an MR is opened or updated |
| Bitbucket Pull Request | Fires when a PR is opened or updated |
| Bitbucket Push | Fires on every push to a branch |
| Schedule (Daily) | Runs the agent once per day |
| Schedule (Hourly) | Runs the agent once per hour |
## Repository selection
For pull request and push triggers, you can scope the automation to specific repositories or allow it to run across all connected repos. Toggle **All repos** off to select specific ones.
## CI check display
For pull request triggers, you can choose whether the automation result appears as a GitHub/GitLab/Bitbucket CI check or as a comment on the PR. Enable **Display as CI check** in the automation settings to block merges until the automation passes.
## Visibility
* **Workspace** — visible and usable by all workspace members
* **Private** — only visible to you
# PR Review
Source: https://docs.blocks.team/using-blocks/products/pr-review
Automatically review every pull request with a coding agent
PR Review automatically runs a coding agent on every pull request — no mention required. Set it up once per workspace and it fires on every new PR across your connected repositories.
## Setup
1. Go to **Settings → PR Review**
2. Enable the global toggle
3. Select which agent to use for reviews
4. Enable or disable review per repository
By default, all connected repositories are enabled. You can toggle individual repos on or off from the same settings page.
Only workspace admins can configure PR Review settings.
## Supported platforms
GitHub, GitLab, and Bitbucket.
## Custom instructions
To tailor what the agent looks for, add a `.blocks/review.md` file to the repository. Blocks will use it as the review prompt for every PR in that repo.
## Responding to review comments
If the agent leaves review comments, you can ask it to address them directly:
`@blocks address the review comments and update the PR`
The agent will push fixes to the same branch without opening a new PR.
## Choosing an agent
Any configured coding agent can be used for PR Review. Claude Code is recommended for most teams — it handles multi-file context well and produces actionable inline comments.
# Environment
Source: https://docs.blocks.team/using-blocks/sandbox/environment
Understanding the Blocks sandbox and how to customize it for your needs
## Overview
When Blocks agents work on your code, they execute in a secure sandbox environment. Each sandbox is based on the Blocks base image, which comes pre-installed with common development tools, programming languages, and CLI utilities. This ensures agents can immediately start working without waiting for basic dependencies.
## Sandbox Compute Properties
Each sandbox environment provides the following compute resources:
* **CPU**: 2 vCPUs
* **Memory**: 4GB RAM
* **Architecture**: AMD64 (x86\_64)
* **Operating System**: Debian Trixie (13.3)
These specifications ensure consistent performance across all Blocks sessions while providing sufficient resources for most development tasks.
If you need a more compute for your workloads, contact us at [dev@blocksorg.com](mailto:dev@blocksorg.com).
## What's Included
The Blocks base image includes a comprehensive set of tools organized by category:
### Build Tools & Compilers
Essential tools for compiling and building code:
* **build-essential** - Meta-package including GCC, G++, Make, and other build tools
* **gcc** - GNU C Compiler
* **g++** - GNU C++ Compiler
* **make** - Build automation tool
* **pkg-config** - Library compilation helper
* **libssl-dev** - SSL development libraries
### Programming Languages & Runtimes
Pre-installed language toolchains:
* **Node.js** - JavaScript runtime with npm package manager
* **Rust** - Stable toolchain via rustup (includes cargo, rustc, rustfmt)
### Version Control & Collaboration
Tools for working with repositories:
* **git** - Distributed version control system
* **gh** - Official GitHub CLI for managing issues, PRs, and repositories
* **glab** (v1.80.4) - GitLab CLI for GitLab workflows
### CLI Utilities
Common command-line tools:
* **curl** - Data transfer tool
* **jq** - JSON processor
* **unzip** - Archive extraction
* **procps** - Process monitoring utilities (ps, top, etc.)
* **sudo** - Privilege escalation
* **ripgrep** - Fast recursive search tool (rg)
* **vim** - Text editor
* **direnv** - Environment variable manager
### Database Tools
* **postgresql-client** - PostgreSQL client tools (psql, pg\_dump, etc.)
### Cloud & Infrastructure
Tools for cloud services and infrastructure:
* **AWS CLI** - Amazon Web Services command-line interface (via pip)
* **Pulumi ESC CLI** - Pulumi Environments, Secrets, and Configuration
### Networking & Remote Access
* **openssh-client** - SSH client for secure remote connections
### System Libraries
Required libraries for graphical and system operations:
* **ca-certificates** - Common CA certificates for SSL/TLS
* **libxcb1** - X11 protocol C-language binding
* **libx11-6** - X11 client library
* **libx11-xcb1** - X11/XCB interop library
## When You Need Additional Tools
While the base image includes many common tools, your project may require additional dependencies:
* **Language-specific tools** - Python (pip packages), Ruby (gems), Go binaries
* **Database clients** - MySQL, MongoDB, Redis clients
* **Build tools** - Gradle, Maven, CMake
* **Testing frameworks** - pytest, Jest, RSpec
* **Custom binaries** - Project-specific CLI tools
* **Cloud provider tools** - Google Cloud SDK, Azure CLI, Terraform
## Using Post-Clone Scripts
To add custom binaries or install additional tools, use post-clone scripts. These scripts run automatically after Blocks clones your repository and before the agent starts working. Learn more about [Post-Clone Scripts](/using-blocks/features/post-cloning).
### Creating a Post-Clone Script
1. Create a `.blocks` directory in your repository root:
```bash theme={null}
mkdir .blocks
```
2. Create a script file named `post-clone` or `post-clone.sh`:
```bash theme={null}
touch .blocks/post-clone
chmod +x .blocks/post-clone
```
3. Add your installation commands:
```bash theme={null}
#!/bin/bash
set -e
# Install Node.js dependencies
npm install
# Install Python dependencies
pip install -r requirements.txt
echo "Post-clone setup complete!"
```
```bash theme={null}
#!/bin/bash
set -e
# Download and install a custom CLI tool
curl -L https://example.com/tool.tar.gz -o /tmp/tool.tar.gz
tar -xzf /tmp/tool.tar.gz -C /usr/local/bin
chmod +x /usr/local/bin/tool
# Verify installation
tool --version
```
```bash theme={null}
#!/bin/bash
set -e
# Install Python and its packages
apt-get update && apt-get install -y python3 python3-pip
pip3 install pytest black flake8
# Install Go
GO_VERSION=1.21.0
curl -L https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz | tar -xz -C /usr/local
export PATH=$PATH:/usr/local/go/bin
# Install project dependencies
npm ci
go mod download
echo "All dependencies installed!"
```
```bash theme={null}
#!/bin/bash
set -e
# Install MySQL client
apt-get update && apt-get install -y mysql-client
# Install MongoDB tools
curl -L https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2204-x86_64-100.9.4.deb -o /tmp/mongo-tools.deb
dpkg -i /tmp/mongo-tools.deb
# Install Redis CLI
apt-get install -y redis-tools
echo "Database tools installed!"
```
### Best Practices
Always start your script with `set -e` to exit immediately if any command fails:
```bash theme={null}
#!/bin/bash
set -e
# Your commands here
```
This prevents the agent from starting work with incomplete dependencies.
Design scripts to run safely multiple times:
```bash theme={null}
#!/bin/bash
set -e
# Check if tool already exists
if ! command -v mytool &> /dev/null; then
echo "Installing mytool..."
curl -L https://example.com/mytool -o /usr/local/bin/mytool
chmod +x /usr/local/bin/mytool
else
echo "mytool already installed"
fi
```
Use lock files to speed up installation:
```bash theme={null}
#!/bin/bash
set -e
# Use npm ci instead of npm install for faster, reproducible installs
npm ci
# Use pip with hashes for security
pip install --require-hashes -r requirements.txt
```
Add echo statements to help debug issues:
```bash theme={null}
#!/bin/bash
set -e
echo "Installing system dependencies..."
apt-get update && apt-get install -y python3-dev
echo "Installing Python packages..."
pip install -r requirements.txt
echo "Running database migrations..."
python manage.py migrate
echo "Post-clone setup complete!"
```
Only install what you need:
```bash theme={null}
#!/bin/bash
set -e
# BAD: Installs everything
apt-get install -y build-essential python3-dev libpq-dev mysql-client mongodb-tools
# GOOD: Only installs what this project needs
apt-get install -y python3-dev libpq-dev
```
## Common Use Cases
### Installing Language Runtimes
```bash Python theme={null}
#!/bin/bash
set -e
# Install Python 3.11
apt-get update && apt-get install -y python3.11 python3.11-dev python3-pip
# Install poetry for dependency management
curl -sSL https://install.python-poetry.org | python3 -
export PATH="/root/.local/bin:$PATH"
# Install project dependencies
poetry install --no-root
```
```bash Go theme={null}
#!/bin/bash
set -e
# Install Go 1.21
GO_VERSION=1.21.0
curl -L https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz | tar -xz -C /usr/local
export PATH=$PATH:/usr/local/go/bin
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
# Download dependencies
go mod download
```
```bash Ruby theme={null}
#!/bin/bash
set -e
# Install Ruby via rbenv
apt-get update && apt-get install -y rbenv ruby-build
# Install specific Ruby version
rbenv install 3.2.0
rbenv global 3.2.0
# Install bundler and dependencies
gem install bundler
bundle install
```
### Installing Build Tools
```bash theme={null}
#!/bin/bash
set -e
# Install CMake
CMAKE_VERSION=3.27.0
curl -L https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh -o /tmp/cmake.sh
sh /tmp/cmake.sh --prefix=/usr/local --skip-license
# Install Ninja build system
apt-get update && apt-get install -y ninja-build
# Build project
cmake -B build -G Ninja
ninja -C build
```
### Setting Up Environment Variables
```bash theme={null}
#!/bin/bash
set -e
# Create .env file from template
cp .env.example .env
# Set development environment
export NODE_ENV=development
export DATABASE_URL=postgresql://localhost:5432/myapp_dev
# Load direnv if .envrc exists
if [ -f .envrc ]; then
direnv allow
fi
# Install dependencies
npm ci
```
## Troubleshooting
Ensure you're using `set -e` at the start of your script. Without it, errors are ignored and the agent continues with incomplete setup.
```bash theme={null}
#!/bin/bash
set -e # Add this!
```
Make sure binaries are installed in a directory on the PATH (`/usr/local/bin`, `/usr/bin`) or export PATH in your script:
```bash theme={null}
export PATH="/custom/path/bin:$PATH"
echo 'export PATH="/custom/path/bin:$PATH"' >> ~/.bashrc
```
Ensure scripts and binaries have execute permissions:
```bash theme={null}
chmod +x /usr/local/bin/mytool
chmod +x .blocks/post-clone
```
Optimize installation steps:
* Use `apt-get install -y` to skip prompts
* Use `npm ci` instead of `npm install`
* Cache downloads when possible
* Only install required dependencies
## Limitations
**Ephemeral Environment:** Each Blocks session runs in a fresh sandbox. Changes made during agent execution (installed packages, generated files) are not persisted between sessions. Always use post-clone scripts for reproducible setup.
**Root Access:** Post-clone scripts run with sudo privileges, allowing installation of system packages. Use this power responsibly and only install trusted software.
## Next Steps
Learn how Plan Mode can help design your post-clone script
Working across multiple repositories with different dependencies
Managing post-clone scripts in GitHub repositories
Create custom skills for common workflows
# Contact & Support
Source: https://docs.blocks.team/using-blocks/support/contact
Get help with Blocks
## Get in Touch
**Fastest response**
Email [dev@blocksorg.com](mailto:dev@blocksorg.com) to set up a private Slack Connect channel with the Blocks team.
[dev@blocksorg.com](mailto:dev@blocksorg.com)
For urgent, production-blocking issues, email [dev@blocksorg.com](mailto:dev@blocksorg.com) with "\[URGENT]" in the subject line or send a message in the Slack Connect channel for immediate reply if set up.
# Question Answering
Source: https://docs.blocks.team/using-blocks/use-cases/question-answering
## Overview
You can ask Blocks questions about your codebase — how things work, where code lives, why changes were made — directly in Slack, Linear, GitHub, or the Blocks dashboard.
If Blocks can't find an answer in your codebase, it will say so rather than guessing.
## Code Questions
Ask about how specific functionality works:
* `@blocks` how does user authentication work in our app?
* `@blocks` where is the payment processing logic implemented?
* `@blocks` what's the difference between UserService and AuthService?
## Architecture Questions
Get insights about system design:
* `@blocks` explain the microservices architecture in our backend
* `@blocks` how do we handle database migrations?
* `@blocks` what's our caching strategy?
## API Questions
Learn about endpoints and integrations:
* `@blocks` list all POST endpoints in the API
* `@blocks` how do we integrate with Stripe?
* `@blocks` what external services does our app depend on?
## Historical Questions
Understand why changes were made:
* `@blocks` why was the authentication flow refactored?
* `@blocks` what was changed in the last deployment?
* `@blocks` show me recent changes to the user model
## Where to Ask
**Slack** — Mention `@blocks` in any channel, or DM the Blocks app directly for private questions.
**Linear** — Ask in issue comments to get context while you work: `@blocks` is there existing code we can reuse for this feature?
**GitHub** — Comment on issues or PRs: `@blocks` what tests exist for this module?
**GitLab** — Comment on issues or merge requests: `@blocks` what tests exist for this module?
**Bitbucket** — Comment on issues or PRs: `@blocks` what tests exist for this module?
**Dashboard** — Use the Blocks dashboard as a chat interface for open-ended exploration, especially when you're not sure yet what to ask.
## Getting References
Blocks doesn't include file links by default, but you can ask for them:
* `@blocks` where is the payment processing logic — include file links
## Multi-Repo Support
Blocks can answer questions that span multiple repositories:
* `@blocks` how does the frontend communicate with the backend API?
* `@blocks` compare the authentication implementation in our web and mobile apps
Learn more about [Multi-Repo Support](/using-blocks/features/multi-repo-support).
# Task Delegation
Source: https://docs.blocks.team/using-blocks/use-cases/task-delegation
## Overview
Blocks lets you delegate technical tasks directly from Slack, Linear, or GitHub. Describe what you need done, and Blocks will handle the implementation — opening a PR or responding in-thread depending on the request.
## Delegating a Task
Mention `@blocks` in any Slack thread, Linear issue, or GitHub PR and describe what you need:
* `@blocks add error handling to the login endpoint`
* `@blocks implement rate limiting for our API, add tests, and update docs`
* `@blocks optimize this query without changing the API response structure`
* `@blocks implement this feature following the existing pattern in UserService`
## Where to Delegate
### From Slack Conversations
Turn discussions into action items:
```
@blocks based on this thread, create a Linear ticket with technical details and implement it
```
### From Linear Issues
Delegate implementation directly:
```
@blocks implement this ticket and address the edge cases we discussed
```
### From GitHub PRs
Request changes or improvements:
```
@blocks address these review comments and update the PR
```
## Task Types You Can Delegate
* **Feature Implementation**: Add new functionality or capabilities
* **Bug Fixes**: Resolve issues and edge cases
* **Refactoring**: Improve code structure and maintainability
* **Testing**: Add or improve test coverage
* **Documentation**: Update README, API docs, or inline comments
* **Performance**: Optimize slow queries or inefficient code
* **Security**: Fix vulnerabilities or improve security posture
* **Infrastructure**: Update configs, dependencies, or deployment scripts
## Following Up
Blocks keeps context within a thread. You can follow up directly:
1. *@blocks implement user profile editing*
2. *(After seeing the response)* Also add profile picture upload support.
If the result isn't right, just tell it what to fix.
## Plan Mode
For large or complex tasks, use Plan Mode to review the approach before Blocks starts coding:
```
@blocks /plan redesign the authentication system to support SSO
```
Learn more about [Plan Mode](/using-blocks/features/plan-mode).
## Learn More
* [Getting Started](/using-blocks/getting-started)
* [Plan Mode](/using-blocks/features/plan-mode)
* [Multi-Repo Support](/using-blocks/features/multi-repo-support)
# Technical Refinement
Source: https://docs.blocks.team/using-blocks/use-cases/technical-refinement
Technical refinement lets your coding agent read your codebase and enrich a vague ticket with real implementation detail — relevant patterns, edge cases, test scenarios, and effort estimates — without you having to dig through the code yourself.
It works in GitHub Issues, Linear, or anywhere you interact with Blocks.
## What you get
A ticket that says:
> Add ability to export reports
becomes:
**Technical Details**
* Implement CSV and PDF export using existing `ReportGenerator` service
* Add new `/api/reports/:id/export` endpoint
* Use existing download pattern from `InvoiceController` (line 145)
* Store exports in S3 with 24-hour expiry
**Edge Cases**
* Handle large reports (>10MB) with streaming
* Rate limit: 5 exports/hour per user
* Validate user permissions for report access
**Testing**
* Unit tests for export formatting
* Integration test for S3 upload
* E2E test for full export flow
**Estimated Complexity:** Medium — 3–5 days
Blocks updates the ticket directly, or posts a comment with questions if the ticket lacks enough context to proceed.
## How to use it
Ask Blocks to refine a ticket in plain language:
* @blocks add technical details to this ticket based on our codebase
* @blocks what are the edge cases and implementation considerations here?
* @blocks break this epic into smaller tickets with technical details
The more context the ticket has (requirements, acceptance criteria, design links), the more specific the output.
## Plan Mode for large features
For complex features, use `/plan` to generate a full implementation plan — architecture decisions, implementation steps, testing strategy, and rollout considerations. See [Plan Mode](/using-blocks/features/plan-mode).
## Related
* [Question Answering](/using-blocks/use-cases/question-answering) — ask about implementation details before refining
* [Task Delegation](/using-blocks/use-cases/task-delegation) — hand off a refined ticket to a coding agent
# Ticket Assignment
Source: https://docs.blocks.team/using-blocks/use-cases/ticket-assignment
## Overview
Assign a Linear or GitHub issue to Blocks and it will implement the changes and open a pull request with a summary of what was done and how to test it. Blocks posts status updates directly to the ticket as it works — you can also monitor progress from the [dashboard](/using-blocks/dashboard).
## Assigning a Ticket
### Linear and GitHub Issues
The flow is the same for both integrations. There are two ways to assign a ticket:
**Native assignment** — Assign the ticket to Blocks using the assignee field. Blocks will start working on it automatically.
**Comment mention** — Mention Blocks in a comment with specific instructions, for example:
* `@blocks implement this ticket`
* `@blocks fix this without changing the public API`
* `@blocks implement this using the existing authentication pattern`
**Tip**: You can set the default behavior for native assignments in **Settings > Integrations > Linear** (or GitHub). Set the default assign mode to "plan" to have Blocks propose an implementation plan before writing any code.
## Plan Mode for Complex Tickets
For larger features or architectural changes, use `/plan` to review the approach before implementation begins:
`@blocks /plan implement user authentication with OAuth`
Blocks will outline its plan as a ticket comment for you to review and refine. Learn more about [Plan Mode](/using-blocks/features/plan-mode).
## Best Practices
* **Write clear acceptance criteria** — the more specific the ticket, the better the output
* **Link related resources** — reference related issues, PRs, or documentation in the ticket
* **Use plan mode for complex work** — collaborate on the approach before code is written
## Related
* [Linear Integration](/using-blocks/integrations/linear) - Set up Blocks with Linear
* [GitHub Integration](/using-blocks/integrations/github) - Set up Blocks with GitHub
* [Plan Mode](/using-blocks/features/plan-mode) - Collaborative planning before implementation
# Welcome to Blocks
Source: https://docs.blocks.team/using-blocks/welcome
A software factory that brings coding agents into GitHub, GitLab, Slack, Linear, and more
Your team already lives in GitHub, Slack, Linear, and Jira. Blocks brings coding agents directly into those tools — no new interface to learn, no context switching.
Mention `@blocks` in any issue, PR, or message and an agent will write code, fix bugs, review PRs, and open pull requests right where the work is happening. Or set up automated workflows that run without any mention at all.
## Three Ways to Use Blocks
Mention `@blocks` in any GitHub, GitLab, Bitbucket, Slack, Linear, or Jira thread. Describe what you need in plain language and get a PR.
Automatically review every pull request with any agent. Set custom instructions per repository — security checks, style guides, test coverage requirements.
Trigger agents on events: CI failures, incoming Slack alerts, new tickets, Playwright test results. Build custom workflows without managing infrastructure.
## What Teams Use It For
Automatically investigates incoming alerts by tracing root causes through logs and your codebase, then posts findings and proposed fixes in the thread.
Accepts ticket assignments, parses requirements, implements the code changes, and opens a pull request — without manual handoff.
Reviews pull requests for bugs, edge cases, and policy violations. Responds to reviewer comments and pushes incremental fixes autonomously.
Translates natural language questions into validated SQL queries against your schema, executes them, and returns structured results with context.
Correlates CloudWatch events with application code to pinpoint failure origins, then surfaces a targeted fix with supporting diagnostic evidence.
Searches across multiple repositories to map how features, auth flows, or edge cases are implemented — useful for onboarding and incident response.
## Enterprise Ready
Blocks supports SSO, comprehensive audit logging, role-based access controls, Zero Data Retention compliance, and full self-hosting. [Contact us](/using-blocks/support/contact) to discuss your requirements.
## Available Agents
Blocks supports multiple coding agents — Claude Code, Codex, OpenCode, Gemini CLI, Cursor CLI, Kimi Code, and Sisyphus. Each has different strengths for different tasks.
## Get Started
The [Getting Started guide](/using-blocks/getting-started) walks through connecting your first repository and making your first request.
Connect your first repository and make your first request
Set up GitHub, Slack, Linear, and more
Automate code review across your repositories
Build event-driven agent workflows
# Overview
Source: https://docs.blocks.team/using-blocks/workspaces/overview
How workspaces organize your repositories, integrations, and team in Blocks
A workspace is your isolated environment in Blocks. Everything — repositories, integrations, API keys, skills, and sessions — belongs to a workspace.
## Types of Workspaces
**Personal workspaces** are created automatically when you sign up. They're for individual use and don't support multiple members.
**Organization workspaces** are for teams. They support multiple members, role-based access, and admin-controlled permissions. You can create one during onboarding or anytime using the workspace switcher in the bottom-left of the dashboard.
## What a Workspace Contains
* **Repositories** — connected repos available to all workspace members
* **Integrations** — platform connections (GitHub, GitLab, Bitbucket, Slack, Linear, and others), configured per workspace
* **API keys & environment variables** — credentials used by coding agents running in this workspace
* **Skills** — reusable prompts shared across the workspace, available on all platforms
* **Sessions** — coding agent sessions are scoped to the workspace they were started in
Each workspace is fully isolated. Repositories, skills, sessions, and settings in one workspace have no effect on another.
## Roles
Organization workspaces have two roles:
* **Admin** — full access to all settings; can invite and remove members, assign roles, and configure workspace-wide permissions
* **Member** — access to repositories, skills, and sessions; visibility into settings is determined by admins
### What Admins Control
Admins manage members and roles from **Settings → Members**, and configure workspace-wide behavior from **Settings → Permissions**.
Admins can restrict what members can see:
* Authentication settings
* API keys
* Environment variables
* Integrations
* Analytics
* MCP servers
Admins can also enforce workspace-wide behaviors:
* Force all sessions to be private
* Require members to connect external accounts before accessing the workspace
* Disable VSCode on sessions
## Switching Workspaces
Click the workspace name in the bottom-left sidebar to open the workspace switcher. Selecting a workspace updates the entire dashboard context — repositories, skills, sessions, and settings all switch to that workspace.
Your sessions, settings, and skills are specific to each workspace. Switching workspaces changes your entire context.
## Troubleshooting
**Cause**: Repository not authorized in the GitHub App installation.
**Solution**:
1. Go to GitHub → Settings → Applications → Blocks
2. Add the repository to your GitHub App installation
3. Refresh the Blocks dashboard
**Cause**: You haven't been invited, or you don't have a Blocks account yet.
**Solution**: Create a Blocks account, then ask your workspace admin to send you an invitation.
## Next Steps
Create shared skills for your workspace
Work across repositories in your workspace
Complete setup guide for new users