Skip to content

Latest commit

 

History

History
254 lines (192 loc) · 5.86 KB

File metadata and controls

254 lines (192 loc) · 5.86 KB

CycleTask Ruby SDK

Official Ruby client for the CycleTask Mail Forwarding API.

Manage domains, aliases, email forwarding rules, and view delivery stats — all from idiomatic Ruby.

Installation

Add to your Gemfile:

gem 'cycletask'

Then run:

bundle install

Or install directly:

gem install cycletask

Quick Start

require 'cycletask'

client = CycleTask::Client.new(api_key: 'ct_live_your_api_key')

# List all domains
domains = client.domains.list
domains.each { |d| puts "#{d.domain}#{d.status}" }

# Create an alias
a = client.aliases.create(
  alias: 'hello',
  domain_id: domains.first.id,
  destinations: ['me@gmail.com']
)
puts "Created: #{a.alias_address}@#{a.domain}"

Configuration

client = CycleTask::Client.new(
  api_key:     'ct_live_your_api_key',  # Required
  base_url:    'https://api.task-cycle.com/api', # Default
  timeout:     30,                       # Seconds, default: 30
  max_retries: 3                         # Retries on 429/5xx, default: 3
)

API Reference

Domains

# List all domains
domains = client.domains.list
# => [#<CycleTask::Types::Domain id="d_1" domain="example.com" status="verified">]

# Create a domain
domain = client.domains.create(domain: 'example.com')

# Get a domain by ID
domain = client.domains.get('d_1')

# Delete a domain
client.domains.delete('d_1')

# Trigger DNS verification
domain = client.domains.verify('d_1')

# Get required DNS records
records = client.domains.dns_records('d_1')
records.each { |r| puts "#{r.type} #{r.name}#{r.value}" }

Aliases

# List aliases (paginated)
result = client.aliases.list(page: 1, limit: 10, search: 'hello')
result.data       # => [CycleTask::Types::Alias, ...]
result.total      # => 42
result.page       # => 1
result.total_pages # => 5
result.next_page? # => true

# Iterate directly
result.each { |a| puts a.alias_address }

# Create an alias
a = client.aliases.create(
  alias: 'hello',
  domain_id: 'd_1',
  destinations: ['me@gmail.com', 'backup@gmail.com'],
  description: 'My main alias',
  privacy_mode: false
)

# Update an alias
a = client.aliases.update('a_1', is_active: false, description: 'Disabled')

# Delete an alias
client.aliases.delete('a_1')

# Get forwarding logs for an alias
logs = client.aliases.logs('a_1')
logs.each { |l| puts "#{l.from}#{l.status}" }

List Parameters

Parameter Type Description
page Integer Page number
limit Integer Items per page
search String Search by alias name
domain String Filter by domain ID
status String Filter: "active"/"inactive"
sort String Sort field

Stats

# Overview statistics
overview = client.stats.overview
# => {"total_forwarded" => 1234, "total_blocked" => 56, ...}

# Chart data
chart = client.stats.chart(days: 30)

# Email logs (paginated)
logs = client.stats.logs(page: 1, limit: 20, status: 'delivered')
logs.each { |l| puts "#{l.subject}#{l.status}" }

Status (Public)

These endpoints do not require authentication.

# Current system status
status = client.status.get
# => {"status" => "operational", "message" => "All systems normal"}

# Uptime metrics
uptime = client.status.uptime

# Recent incidents
incidents = client.status.incidents

Response Objects

All API responses are wrapped in typed objects with method accessors:

domain = client.domains.get('d_1')
domain.id          # => "d_1"
domain.domain      # => "example.com"
domain.status      # => "verified"
domain.is_verified # => true
domain.to_h        # => {"id" => "d_1", "domain" => "example.com", ...}
domain["id"]       # => "d_1" (bracket access for any field)

Paginated responses implement Enumerable:

result = client.aliases.list
result.size        # => 20
result.total       # => 142
result.next_page?  # => true
result.map(&:id)   # => ["a_1", "a_2", ...]

Error Handling

All errors inherit from CycleTask::Error:

begin
  client.domains.create(domain: 'bad')
rescue CycleTask::AuthenticationError => e
  # 401 — invalid or missing API key
  puts e.message
rescue CycleTask::ValidationError => e
  # 422 — validation failed
  puts e.errors  # => {"domain" => ["is invalid"]}
rescue CycleTask::NotFoundError => e
  # 404 — resource not found
rescue CycleTask::RateLimitError => e
  # 429 — rate limited (auto-retried by default)
rescue CycleTask::ServerError => e
  # 5xx — server error (auto-retried by default)
rescue CycleTask::TimeoutError => e
  # Request timed out
rescue CycleTask::ConnectionError => e
  # Could not connect
rescue CycleTask::Error => e
  # Catch-all for any other API error
  puts e.status  # HTTP status code
  puts e.body    # Parsed response body
end

Error Hierarchy

CycleTask::Error
├── CycleTask::AuthenticationError  (401)
├── CycleTask::NotFoundError        (404)
├── CycleTask::ValidationError      (422)
├── CycleTask::RateLimitError       (429)
├── CycleTask::ServerError          (5xx)
├── CycleTask::TimeoutError
└── CycleTask::ConnectionError

Automatic Retries

Requests that receive 429 (rate limit) or 5xx (server error) responses are automatically retried with exponential backoff:

  • Attempt 1: immediate
  • Retry 1: 1 second delay
  • Retry 2: 2 second delay
  • Retry 3: 4 second delay

If the server provides a Retry-After header, that value is used instead. Configure with max_retries: 0 to disable.

Requirements

  • Ruby 3.0+
  • No external dependencies (uses stdlib net/http and json)

Development

git clone https://github.com/cycletask/ruby-sdk.git
cd ruby-sdk
bundle install
bundle exec rspec

License

MIT License. See LICENSE for details.