← Back to Help

REST API Reference

The Isurus REST API is available at /api/v1/. All responses return JSON.

Authentication

Authenticate API requests using a Bearer token in the Authorization header:

curl -H "Authorization: Bearer YOUR_TOKEN" \
     https://your-isurus-instance/api/v1/user

Create tokens under Settings > API Tokens in the web UI, or via the API itself.

Token Scopes

Scope Access
read Read repositories, issues, pull requests, org info
write Create/update repositories, issues, pull requests, webhooks
admin Delete repositories, administrative actions

Some endpoints are public (no token required). Private repositories require at least read scope.

Response Format

Success

{
  "data": { ... }
}

List with Pagination

{
  "data": [ ... ],
  "pagination": {
    "page": 1,
    "per_page": 30,
    "total": 42
  }
}

Error

{
  "error": {
    "code": "not_found",
    "message": "Repository not found"
  }
}

Pagination

List endpoints accept page and per_page query parameters:

Parameter Default Max
page 1
per_page 30 100

User

Get Current User

GET /api/v1/user

Scope: read

Returns the authenticated user's profile.

Response:

{
  "data": {
    "id": 1,
    "username": "chris",
    "email": "chris@example.com",
    "display_name": "Chris",
    "is_admin": true,
    "avatar_url": "",
    "created_at": "2025-01-15T10:00:00Z"
  }
}

API Tokens

List Tokens

GET /api/v1/user/tokens

Scope: read

Returns all tokens for the authenticated user (token values are not included).

Create Token

POST /api/v1/user/tokens

Scope: read

Body:

{
  "name": "CI Pipeline",
  "scopes": ["read", "write"],
  "expires_at": "2026-12-31T23:59:59Z"
}

The expires_at field is optional (RFC 3339 format). Omit it for a non-expiring token.

Response: Returns the token object with a token field containing the plaintext value. This is the only time the token value is returned — store it securely.

Revoke Token

DELETE /api/v1/user/tokens/:id

Scope: read

Permanently deletes the token.


Organizations

Get Organization

GET /api/v1/orgs/:org

Auth: Optional (public endpoint)

Response:

{
  "data": {
    "id": 1,
    "name": "leafscale",
    "description": "Leafscale, LLC",
    "created_at": "2025-01-15T10:00:00Z"
  }
}

List Organization Repositories

GET /api/v1/orgs/:org/repos

Auth: Optional. Without auth, only public repos are returned. With auth, private repos visible to the user are included.

List Organization Members

GET /api/v1/orgs/:org/members

Scope: read (must be an org member)

Returns members with their roles.

Response:

{
  "data": [
    {
      "user": { "id": 1, "username": "chris", ... },
      "role": "owner"
    }
  ]
}

Repositories

Get Repository

GET /api/v1/repos/:org/:repo

Auth: Optional (required for private repos)

Response:

{
  "data": {
    "id": 1,
    "name": "my-project",
    "org": "leafscale",
    "description": "A cool project",
    "is_private": false,
    "created_at": "2025-01-15T10:00:00Z",
    "updated_at": "2025-01-20T14:30:00Z"
  }
}

Create Repository

POST /api/v1/orgs/:org/repos

Scope: write (must be an org member)

Body:

{
  "name": "new-repo",
  "description": "My new repository",
  "is_private": false
}

Update Repository

PATCH /api/v1/repos/:org/:repo

Scope: write (must be an org owner)

Body (all fields optional):

{
  "description": "Updated description",
  "is_private": true
}

Delete Repository

DELETE /api/v1/repos/:org/:repo

Scope: admin (must be an org owner)

Warning: This permanently deletes the repository and all its data.


Repository Contents

List Directory

GET /api/v1/repos/:org/:repo/tree/*path

Auth: Optional (required for private repos)

Query parameters:

Parameter Default Description
rev tip Revision (changeset hash, tag, bookmark, or tip)

Response:

{
  "data": [
    { "name": "src", "is_dir": true, "size": 0 },
    { "name": "README.md", "is_dir": false, "size": 1234 }
  ]
}

Get File Contents

GET /api/v1/repos/:org/:repo/blob/*path

Auth: Optional (required for private repos)

Query parameters:

Parameter Default Description
rev tip Revision

Response:

{
  "data": {
    "path": "README.md",
    "content": "# My Project\n...",
    "size": 1234
  }
}

Get Commit Log

GET /api/v1/repos/:org/:repo/log

Auth: Optional (required for private repos)

Query parameters:

Parameter Default Description
rev tip Starting revision
page 1 Page number
per_page 30 Results per page

Get Changeset

GET /api/v1/repos/:org/:repo/changeset/:rev

Auth: Optional (required for private repos)

Returns changeset details including the diff.

Response:

{
  "data": {
    "node": "abc123...",
    "short_node": "abc123",
    "rev": 42,
    "author": "Chris <chris@example.com>",
    "date": "2025-01-20T14:30:00Z",
    "message": "Full commit message",
    "subject": "First line of commit",
    "branch": "default",
    "tags": ["v1.0"],
    "diff": "diff -r ..."
  }
}

Issues

Issues have three states: new, open, and closed. New issues start in the new state. Triaging an issue moves it to open. Closing an issue moves it to closed. Reopening a closed issue returns it to open.

Note: Label management, issue templates, assignee management, and issue pinning are currently available through the web interface only. API endpoints for these features are planned for a future release.

List Issues

GET /api/v1/repos/:org/:repo/issues

Auth: Optional (required for private repos)

Query parameters:

Parameter Default Description
state open Filter by state: new, open, or closed
page 1 Page number
per_page 30 Results per page

Response:

{
  "data": [
    {
      "id": 1,
      "number": 1,
      "title": "Bug: something is broken",
      "body": "Steps to reproduce...",
      "state": "new",
      "author": {
        "id": 1,
        "username": "chris",
        "email": "chris@example.com",
        "display_name": "Chris",
        "is_admin": true,
        "avatar_url": "",
        "created_at": "2025-01-15T10:00:00Z"
      },
      "labels": [
        { "id": 1, "name": "bug", "color": "#DC2626" }
      ],
      "created_at": "2025-01-20T14:30:00Z",
      "updated_at": "2025-01-20T14:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 30,
    "total": 5
  }
}

The labels array is included when the issue has labels assigned. The author object is included when available.

Get Issue

GET /api/v1/repos/:org/:repo/issues/:number

Auth: Optional (required for private repos)

Returns the issue with all comments.

Response:

{
  "data": {
    "id": 1,
    "number": 1,
    "title": "Bug: something is broken",
    "body": "Steps to reproduce...",
    "state": "open",
    "author": {
      "id": 1,
      "username": "chris",
      "display_name": "Chris"
    },
    "labels": [
      { "id": 1, "name": "bug", "color": "#DC2626" }
    ],
    "comments": [
      {
        "id": 1,
        "body": "I can reproduce this on rev abc123.",
        "author": {
          "id": 2,
          "username": "alice",
          "display_name": "Alice"
        },
        "created_at": "2025-01-21T09:00:00Z"
      }
    ],
    "created_at": "2025-01-20T14:30:00Z",
    "updated_at": "2025-01-21T09:00:00Z"
  }
}

The comments array is only included when the issue has comments.

Create Issue

POST /api/v1/repos/:org/:repo/issues

Scope: write

Body:

{
  "title": "Bug: something is broken",
  "body": "Steps to reproduce..."
}

New issues are created in the new state.

Update Issue

PATCH /api/v1/repos/:org/:repo/issues/:number

Scope: write (must be author or org owner)

Body (all fields optional):

{
  "title": "Updated title",
  "body": "Updated description",
  "state": "closed"
}

Set state to "closed" to close the issue, or "open" to reopen a closed issue.

Comment on Issue

POST /api/v1/repos/:org/:repo/issues/:number/comments

Scope: write

Body:

{
  "body": "Thanks for the report, this is fixed in rev abc123."
}

Pull Requests

List Pull Requests

GET /api/v1/repos/:org/:repo/pulls

Auth: Optional (required for private repos)

Query parameters:

Parameter Default Description
status open Filter: open, closed, or merged
page 1 Page number
per_page 30 Results per page

Get Pull Request

GET /api/v1/repos/:org/:repo/pulls/:number

Auth: Optional (required for private repos)

Returns the PR with all comments.

Create Pull Request

POST /api/v1/repos/:org/:repo/pulls

Scope: write (must be an org member)

Body:

{
  "title": "Add new feature",
  "body": "This PR adds...",
  "source_bookmark": "my-feature",
  "target_branch": "default"
}

The target_branch defaults to "default" if omitted.

Update Pull Request

PATCH /api/v1/repos/:org/:repo/pulls/:number

Scope: write (must be author or org owner)

Body (all fields optional):

{
  "title": "Updated title",
  "body": "Updated description"
}

Merge Pull Request

POST /api/v1/repos/:org/:repo/pulls/:number/merge

Scope: write (must be an org member)

Merges the source bookmark into the target branch.

Comment on Pull Request

POST /api/v1/repos/:org/:repo/pulls/:number/comments

Scope: write

Body:

{
  "body": "LGTM, ship it!"
}

CI/CD Pipelines

List Pipelines

GET /api/v1/repos/:org/:repo/pipelines

Auth: Optional (required for private repos)

Query parameters:

Parameter Default Description
status (all) Filter by status: pending, running, success, failure, cancelled
page 1 Page number
per_page 20 Results per page (max 50)

Response:

{
  "total": 42,
  "page": 1,
  "per_page": 20,
  "pipelines": [
    {
      "id": 1,
      "number": 7,
      "commit_node": "a1b2c3d4e5f6...",
      "branch": "default",
      "event": "push",
      "status": "success",
      "trigger_by": 1,
      "trigger_user": { "id": 1, "username": "chris" },
      "created_at": "2025-01-20T14:30:00Z",
      "started_at": "2025-01-20T14:30:05Z",
      "finished_at": "2025-01-20T14:32:10Z"
    }
  ]
}

Get Pipeline

GET /api/v1/repos/:org/:repo/pipelines/:number

Auth: Optional (required for private repos)

Returns the pipeline with all steps.

Response:

{
  "id": 1,
  "number": 7,
  "commit_node": "a1b2c3d4e5f6...",
  "branch": "default",
  "event": "push",
  "status": "success",
  "trigger_by": 1,
  "trigger_user": { "id": 1, "username": "chris" },
  "created_at": "2025-01-20T14:30:00Z",
  "started_at": "2025-01-20T14:30:05Z",
  "finished_at": "2025-01-20T14:32:10Z",
  "steps": [
    {
      "id": 1,
      "number": 1,
      "name": "test",
      "image": "golang:1.26",
      "status": "success",
      "exit_code": 0,
      "started_at": "2025-01-20T14:30:05Z",
      "finished_at": "2025-01-20T14:32:10Z"
    }
  ]
}

Cancel Pipeline

POST /api/v1/repos/:org/:repo/pipelines/:number/cancel

Scope: write (must be an org member)

Cancels a running or pending pipeline and all its pending/running steps.

Re-run Pipeline

POST /api/v1/repos/:org/:repo/pipelines/:number/rerun

Scope: write (must be an org member)

Creates a new pipeline from the same commit. Returns the new pipeline object.

Note: Pipeline deletion (single, bulk, and delete-all-failed) is available through the web interface only. API endpoints for pipeline deletion are planned for a future release.


CI Agent API

These endpoints are used by isurus-agent workers. They use agent token authentication (not user API tokens).

Agent Authentication

Agents authenticate with a Bearer token issued during agent registration:

curl -H "Authorization: Bearer AGENT_TOKEN" \
     -X POST https://your-isurus-instance/api/v1/ci/poll

Poll for Job

POST /api/v1/ci/poll

Claims the next pending pipeline. Returns 204 No Content if no work is available.

Response (200):

{
  "pipeline_id": 42,
  "number": 7,
  "commit_node": "a1b2c3d4e5f6...",
  "branch": "default",
  "event": "push",
  "clone_url": "https://hg.example.com/leafscale/isurus",
  "org": "leafscale",
  "repo": "isurus",
  "steps": [
    {
      "id": 1,
      "number": 1,
      "name": "test",
      "image": "golang:1.26",
      "commands": ["go test -v ./..."],
      "environment": { "CGO_ENABLED": "0" },
      "status": "pending"
    }
  ],
  "ci_env": {
    "CI": "true",
    "CI_PIPELINE_ID": "42",
    "CI_COMMIT": "a1b2c3d4e5f6...",
    "CI_BRANCH": "default",
    "CI_REPO": "leafscale/isurus"
  }
}

Heartbeat

POST /api/v1/ci/heartbeat

Updates the agent's last-seen timestamp. Called on each poll cycle.

Update Step Status

PUT /api/v1/ci/steps/:id/status

Body:

{
  "status": "running",
  "exit_code": 0,
  "started_at": "2025-01-20T14:30:05Z",
  "finished_at": "2025-01-20T14:32:10Z"
}

Status values: pending, running, success, failure, skipped, cancelled

Update Pipeline Status

PUT /api/v1/ci/pipelines/:id/status

Body:

{
  "status": "success",
  "finished_at": "2025-01-20T14:32:10Z"
}

Status values: pending, running, success, failure, cancelled, error

Append Step Log

POST /api/v1/ci/steps/:id/log

Appends raw log data to the step's log file. Body is raw text (not JSON). Maximum 1MB per request.


Webhooks

List Webhooks

GET /api/v1/repos/:org/:repo/webhooks

Scope: write (must be an org owner)

Create Webhook

POST /api/v1/repos/:org/:repo/webhooks

Scope: write (must be an org owner)

Body:

{
  "url": "https://example.com/webhook",
  "secret": "my-secret",
  "events": ["push", "issue", "pull_request"]
}

Event types: push, issue, pull_request

Update Webhook

PATCH /api/v1/repos/:org/:repo/webhooks/:webhook_id

Scope: write (must be an org owner)

Body (all fields optional):

{
  "url": "https://example.com/new-endpoint",
  "events": ["push"],
  "is_active": false
}

Delete Webhook

DELETE /api/v1/repos/:org/:repo/webhooks/:webhook_id

Scope: write (must be an org owner)

List Webhook Deliveries

GET /api/v1/repos/:org/:repo/webhooks/:webhook_id/deliveries

Scope: write (must be an org owner)

Returns the 20 most recent deliveries.

Response:

{
  "data": [
    {
      "id": 1,
      "event_type": "push",
      "response_code": 200,
      "duration_ms": 150,
      "success": true,
      "delivered_at": "2025-01-20T14:30:00Z"
    }
  ]
}

Releases

List Releases

GET /api/v1/repos/:org/:repo/releases

Returns paginated releases. Draft releases are only visible to org members.

Query parameters: page, per_page

Get Release

GET /api/v1/repos/:org/:repo/releases/:id

Get Release by Tag

GET /api/v1/repos/:org/:repo/releases/tags/:tag

Create Release

POST /api/v1/repos/:org/:repo/releases

Requires: write scope

{
  "tag_name": "v1.0.0",
  "name": "Version 1.0.0",
  "body": "Release notes in markdown",
  "draft": false,
  "prerelease": false
}

Update Release

PATCH /api/v1/repos/:org/:repo/releases/:id

Requires: write scope. All fields are optional — only provided fields are updated.

Delete Release

DELETE /api/v1/repos/:org/:repo/releases/:id

Requires: write scope. Deletes the release and all its attachments.

List Release Assets

GET /api/v1/repos/:org/:repo/releases/:id/assets

Upload Release Asset

POST /api/v1/repos/:org/:repo/releases/:id/assets

Requires: write scope. Send as multipart/form-data with field name attachment.

Delete Release Asset

DELETE /api/v1/repos/:org/:repo/releases/:id/assets/:asset_id

Requires: write scope