AtlasWork, planned itself.

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

    System status
    • SSO
    • SCIM
    • Two-factor sign-in
    • Audit log

    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

    You are in control of your cookies

    Atlas uses strictly necessary cookies to keep you signed in. With your consent, we add anonymized product analytics, conversion attribution, and remembered preferences. Change your mind any time at /privacy/cookies.

    Off until you agree · Change it any time

    • Necessaryalways on
    • Analyticsopt-in
    • Marketingopt-in
    • Preferencesopt-in
    Skip to documentation
    Docs
    Back to Atlas

    Start here

    • Overview

    Developer

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

    Webhooks

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

    Connect

    • Connectors
    • Integrations

    Product

    • Collaboration and chat
    • Signing in and security
    • Client portal

    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 the OpenAPI document the API serves at /v1/openapi.json, so method names track the API exactly.

    Method naming

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

    Install

    Atlas does not publish this client as a gem. Download the single file from Atlas itself; it needs nothing beyond the standard library.

    bash
    # Download the single-file client next to your code.
    curl -fsSLO https://atlas.wrxstack.com/sdk/ruby/atlas.rb
    bash
    # Check it arrived intact.
    curl -fsSLO https://atlas.wrxstack.com/sdk/ruby/atlas.rb.sha256
    shasum -a 256 -c atlas.rb.sha256

    Do not install atlas-sdk from RubyGems

    Any gem named atlas-sdk on RubyGems is not published by Atlas. Use only the file served from atlas.wrxstack.com, and download it again when the API changes.

    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_relative '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 client never widens a token. Create a token with the narrowest scopes that work, and widen them only when a call is refused.

    Quickstart

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

    ruby
    require_relative 'atlas'
    
    client = Atlas::Client.new(token: ENV.fetch('ATLAS_TOKEN'))
    
    # List endpoints answer an { "items" => [...], "nextCursor" => ... } envelope.
    page = client.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.get_tasks(query: {
      projectId: 'prj_123',
      status: 'IN_PROGRESS',
      assigneeId: 'usr_42',
      limit: 50,
    })
    tasks = page['items']

    Get a task

    ruby
    # Path parameters are the leading arguments, filled in and URL-encoded.
    task = client.get_tasks_by_id(task_id)
    puts task['title'], task['status']

    Path parameters

    Every route with a path parameter takes it as a leading argument: get_tasks_by_id(task_id) calls /v1/tasks/{id} with the id URL-encoded into the path. The same holds for update and delete.

    Create a task

    ruby
    created = client.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.patch_tasks_by_id(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.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.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.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.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_relative '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', '/v1/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 a staging server.

    ruby
    require_relative 'atlas'
    
    client = Atlas::Client.new(
      token: ENV.fetch('ATLAS_TOKEN'),
      base_url: 'https://api-atlas.wrxstack.com', # the default; override for staging
      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 /v1/tasksget_tasks(query:)
    Create taskPOST /v1/taskspost_tasks(body:)
    Get taskGET /v1/tasks/{id}get_tasks_by_id(id)
    Update taskPATCH /v1/tasks/{id}patch_tasks_by_id(id, body:)
    Delete taskDELETE /v1/tasks/{id}delete_tasks_by_id(id)
    List projectsGET /v1/projectsget_projects(query:)
    Create projectPOST /v1/projectspost_projects(body:)

    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