AtlasWork, planned itself.

The AI-native, all-in-one work platform. Tasks, projects, CRM, contracts, and analytics in one calm workspace.

All systems operational
  • SOC 2 II
  • ISO 27001
  • HIPAA
  • GDPR

Product

  • Overview
  • PDF tools
  • Diagram tools
  • People & HR
  • Integrations
  • Marketplace
  • Pricing

Resources

  • Guides
  • Glossary
  • Compare
  • Docs
  • API reference
  • Support
  • Changelog
  • Status

Company

  • About
  • Careers
  • Press
  • Contact

Legal & trust

  • Trust center
  • Security
  • Privacy
  • Terms
  • DPA
  • GDPR
  • SLA
  • Refunds
  • Google API data
Atlas, a product by wrxstack.com·© 2026 wrxstack·All rights reserved
PrivacyTermsSecurityStatus
Skip to documentation
Docs
Back to Atlas

Start here

  • Overview

Developer

  • REST API guide
  • Authentication
  • API reference
  • MCP (AI agents)
  • SDKs
  • Quick actions

Webhooks

  • Overview
  • Quickstart
  • Events
  • Payloads and headers
  • Security and signing
  • Delivery and retries
  • Managing via API

Connect

  • Connectors
  • Integrations

Product

  • Collaboration and chat

Reference

  • Glossary
  • Keyboard shortcuts
  • Module reference

SDK

Ruby SDK

A dependency-free client for the Atlas REST API, generated from the OpenAPI spec. Works in any Ruby 3.0+ project.

Language
PythonGoRuby
One REST surface, three idiomatic clients.

Overview

The Atlas Ruby SDK wraps the same REST surface documented in the API guide. Atlas::Client exposes a method per operation, handles bearer-token auth, and raises a typed error hierarchy on non-2xx responses. It is generated from docs/atlas-openapi.json, so method names track the API exactly.

Method naming

Each method is the snake_case of the OpenAPI operationId. Listing tasks (operation atlas_get_tasks) is client.atlas_get_tasks(...). The methods overview maps the common resources.

Install

Published on RubyGems. Add it with Bundler or install the gem directly.

bash
gem install atlas-sdk
bash
bundle add atlas-sdk

Authentication

Every request carries a Personal Access Token as a bearer credential. Tokens start with atlas_pat_ and are scope-bounded and revocable.

ruby
require 'atlas'

# Read the token from the environment - never hard-code it in source.
client = Atlas::Client.new(token: ENV.fetch('ATLAS_TOKEN'))

Scopes are enforced server-side

A token only reaches the routes its scopes allow. A call outside them fails with 403 (insufficient_scope) and raises Atlas::ForbiddenError - the SDK never widens a token. Mint the narrowest scope set that works and widen only when a call is refused.

Quickstart

One authenticated request, end to end. Set ATLAS_TOKEN in your environment first.

ruby
require 'atlas'

client = Atlas::Client.new(token: ENV.fetch('ATLAS_TOKEN'))

# List endpoints answer an { "items" => [...], "nextCursor" => ... } envelope.
page = client.atlas_get_tasks(query: { limit: 5 })
page['items'].each do |task|
  puts "#{task['id']} #{task['title']}"
end

Core examples

The everyday task and project operations. Reads answer an { items, nextCursor } envelope; writes take a body hash.

List tasks with filters

ruby
# Filters map straight to the REST query string; the server ignores unknown keys.
page = client.atlas_get_tasks(query: {
  projectId: 'prj_123',
  status: 'IN_PROGRESS',
  assigneeId: 'usr_42',
  limit: 50,
})
tasks = page['items']

Get a task

ruby
# Single-resource routes carry the id in the path. Client#request is public, so
# call it directly to fill the id in.
task = client.request('GET', "/tasks/#{task_id}")
puts task['title'], task['status']

Single-resource routes

The generated atlas_get_tasks_record method targets /tasks/{id} but does not interpolate the id, so fill it through the public request method as shown. The same applies to update and delete.

Create a task

ruby
created = client.atlas_post_tasks(body: {
  projectId: 'prj_123',
  title: 'Review Q3 forecast',
  priority: 'HIGH',
  dueOn: '2026-05-01T17:00:00Z',
})
puts "created #{created['id']}"

Update a task

ruby
updated = client.request('PATCH', "/tasks/#{task_id}", body: { status: 'DONE' })

Cursor-based pagination

ruby
def each_task(client, **filters)
  cursor = nil
  loop do
    query = filters.merge(limit: 100)
    query[:cursor] = cursor if cursor
    page = client.atlas_get_tasks(query: query)
    page['items'].each { |task| yield task }
    cursor = page['nextCursor']
    break unless cursor
  end
end

each_task(client, projectId: 'prj_123') { |task| puts task['id'] }

List projects

ruby
projects = client.atlas_get_projects(query: { limit: 50 })
projects['items'].each do |project|
  puts "#{project['id']} #{project['name']}"
end

Error handling

Every non-2xx response raises a subclass of Atlas::ApiError. The error carries the HTTP status and the parsed problem+json body.

ruby
begin
  client.atlas_post_tasks(body: { title: '' })
rescue Atlas::ForbiddenError => err
  # 403: the token is missing a scope. The scopes are in the problem body.
  puts "need scopes: #{(err.body || {})['requiredScopes']}"
rescue Atlas::UnauthorizedError
  # 401: token invalid or revoked - re-authenticate.
  raise
rescue Atlas::ApiError => err
  # 409 / 422 / 429 and anything else land here with #status set.
  problem = err.body || {}
  warn "#{err.status} #{problem['title']} #{problem['requestId']}"
  (problem['errors'] || []).each { |f| warn "#{f['path']}: #{f['message']}" } if err.status == 422
  raise
end
HTTP statusError classMeaning
400Atlas::BadRequestErrorMalformed request or query.
401Atlas::UnauthorizedErrorToken missing, invalid, expired, or revoked.
403Atlas::ForbiddenErrorToken is missing a required scope.
404Atlas::NotFoundErrorResource does not exist or is out of tenant.
409, 422, 429ApiError (base)No dedicated class - branch on the status code and read the body.
5xxAtlas::ServerErrorTransient server fault. Safe to retry.

Only the statuses above get a dedicated class. Conflicts (409), validation failures (422), and rate limits (429) raise the base Atlas::ApiError, so branch on err.status and read the body. Every error wraps the API's RFC 9457 problem+json envelope:

http
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
x-request-id: f47ac10b-58cc-4372-a567-0e02b2c3d479

{
  "type": "https://atlas.dev/errors/insufficient-scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "This Personal Access Token is missing one of the required scopes: tasks:write.",
  "requiredScopes": ["tasks:write"],
  "grantedScopes": ["tasks:read"],
  "requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

err.body is that parsed JSON: type, title, status, detail, and a requestId to quote in support tickets. A 422 adds an errors array; a 429 adds a rateLimit object.


Retries and rate limits

Retry 429 and 5xx; never retry 4xx you caused. The 429 body carries the retry delay, so honour it before falling back to backoff.

ruby
RETRYABLE = [429, 500, 502, 503, 504].freeze

def with_retries(max_attempts: 5)
  attempt = 0
  begin
    attempt += 1
    yield
  rescue Atlas::ApiError => err
    raise if !RETRYABLE.include?(err.status) || attempt >= max_attempts

    rate_limit = (err.body || {})['rateLimit'] || {}
    # The 429 body carries the authoritative retry delay, in seconds.
    delay = rate_limit['retryAfterSec'] || [2**(attempt - 1), 30].min + rand
    sleep(delay)
    retry
  end
end

page = with_retries { client.atlas_get_tasks(query: { limit: 50 }) }

Back off with jitter

When no retryAfterSec is present, use exponential backoff with a random jitter so a fleet of clients does not retry in lockstep. Cap total attempts - a persistent 429 means you need a higher tier, not more retries.

Idempotency

Every write accepts an Idempotency-Key header. Replaying the same key within 24h returns the original response verbatim - safe retries on flaky networks.

ruby
require 'atlas'
require 'json'
require 'net/http'
require 'securerandom'
require 'uri'

module Atlas
  # The generated client sends a fixed header set; this thin extension adds an
  # Idempotency-Key to writes so a retried request replays (24h) instead of
  # creating a duplicate.
  class IdempotentClient < Client
    def write(method, path, body, key:)
      uri = URI.parse("#{@base_url}#{path}")
      req = Net::HTTP.const_get(method.capitalize).new(uri)
      req['Authorization'] = "Bearer #{@token}"
      req['Content-Type'] = 'application/json'
      req['Idempotency-Key'] = key
      req.body = JSON.generate(body)

      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = uri.scheme == 'https'
      JSON.parse(http.request(req).body)
    end
  end
end

client = Atlas::IdempotentClient.new(token: ENV.fetch('ATLAS_TOKEN'))

# Generate one key per logical write and reuse it on every retry.
key = SecureRandom.uuid
task = client.write('POST', '/tasks', { projectId: 'prj_123', title: 'Ship it' }, key: key)

Reuse the key on retry

Generate the key once per logical operation and pass the same value on every retry. A new key is a new operation, so a retried create with a fresh key produces a duplicate. See the REST guide for the full replay semantics.

Configuration

The constructor takes the base URL and separate connect and read timeouts. Override the base URL for staging or a self-hosted deployment.

ruby
require 'atlas'

client = Atlas::Client.new(
  token: ENV.fetch('ATLAS_TOKEN'),
  base_url: 'https://api.atlas.app/v1', # override for staging or self-hosted
  open_timeout: 10,                     # seconds to establish the connection
  read_timeout: 30,                     # seconds to wait for the response
)

For a custom User-Agent, request id, or any other header, extend the client as in Idempotency - the transport is a single method you can wrap.


Methods overview

The common task and project operations. The full set - one method per operation - lives in the API reference.

ResourceRESTSDK method
List tasksGET /tasksatlas_get_tasks(query:)
Create taskPOST /tasksatlas_post_tasks(body:)
Get taskGET /tasks/{id}atlas_get_tasks_record *
Update taskPATCH /tasks/{id}atlas_patch_tasks_record *
Delete taskDELETE /tasks/{id}atlas_delete_tasks_record *
List projectsGET /projectsatlas_get_projects(query:)
Create projectPOST /projectsatlas_post_projects(body:)

* Single-resource methods exist but send the literal {id} path; fill the id through the public request method (see Get a task).


Next steps

The SDK is a thin, faithful layer over the REST API. When you need the exact shape of a request or response, go to the source.

  • REST API guideAuth, pagination, idempotency, and rate limits at the protocol level - the contract every SDK speaks./docs/api
  • API referenceEvery operation, request body, and response shape - the source of truth for method arguments./docs/api/reference
  • MCP for AI agentsHand the same operations to an AI agent over the Model Context Protocol./docs/mcp

On this page

  • Overview
  • Install
  • Authentication
  • Quickstart
  • Core examples
  • List tasks
  • Get a task
  • Create a task
  • Update a task
  • Pagination
  • List projects
  • Error handling
  • Retries and rate limits
  • Idempotency
  • Configuration
  • Methods overview
  • Next steps