Skip to content

Repository files navigation

Attio Ruby SDK

Gem VersionBuild StatusDocumentation

A Ruby SDK for the Attio API. This gem provides a simple and intuitive interface for interacting with Attio's CRM platform.

Table of Contents

Installation

Add this line to your application's Gemfile:

gem'attio-ruby'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install attio-ruby

Quick Start

require'attio'# Configure the clientAttio.configuredo |config|
config.api_key=ENV['ATTIO_API_KEY']end# Create a personperson=Attio::Person.create(first_name: "John",last_name: "Doe",email: "john@example.com")# Search for companiescompanies=Attio::Company.search("tech")

Configuration

The gem can be configured globally or on a per-request basis:

Global Configuration

Attio.configuredo |config|
# Requiredconfig.api_key="your_api_key"# Optionalconfig.api_base="https://api.attio.com"# Defaultconfig.api_version="v2"# Defaultconfig.timeout=30# Request timeout in secondsconfig.max_retries=3# Number of retries for failed requestsconfig.debug=false# Enable debug loggingconfig.logger=Logger.new(STDOUT)# Custom loggerend

Environment Variables

The gem automatically reads configuration from environment variables:

  • ATTIO_API_KEY - Your API key
  • ATTIO_API_BASE - API base URL (optional)
  • ATTIO_DEBUG - Enable debug mode (optional)

Per-Request Configuration

# Override configuration for a single requestperson=Attio::Person.create(first_name: "Jane",last_name: "Doe",api_key: "different_api_key")

Authentication

API Key Authentication

The simplest way to authenticate is using an API key:

Attio.configuredo |config|
config.api_key="your_api_key"end

OAuth 2.0 Authentication

For user-facing applications, use OAuth 2.0. The gem includes OAuth support, but for a complete OAuth integration example, see our companion Rails application (coming soon).

# Initialize OAuth clientoauth_client=Attio::OAuth::Client.new(client_id: ENV['ATTIO_CLIENT_ID'],client_secret: ENV['ATTIO_CLIENT_SECRET'],redirect_uri: "https://yourapp.com/callback")# Generate authorization URLauth_data=oauth_client.authorization_url(scopes: %w[record:readrecord:write],state: "random_state")redirect_toauth_data[:url]# Exchange code for tokentoken=oauth_client.exchange_code_for_token(code: params[:code])# Use the tokenAttio.configuredo |config|
config.api_key=token.access_tokenend

Basic Usage

Working with Objects

Objects represent the different types of records in your workspace (e.g., People, Companies).

# List all objectsobjects=Attio::Object.listobjects.eachdo |object|
puts"#{object.plural_noun} (#{object.api_slug})"end# Get a specific objectpeople_object=Attio::Object.retrieve("people")putspeople_object.name# => "People"

Managing Records

Records are instances of objects (e.g., individual people or companies). The gem provides typed classes (Attio::Person, Attio::Company) that inherit from TypedRecord, offering a cleaner interface than the generic Attio::Record class.

Complex Attributes

The gem provides convenient methods for working with complex attributes. You can use the simplified interface or the raw API format:

Simple Interface (Recommended):

# The gem handles the complex structure for youperson=Attio::Person.create(first_name: "John",last_name: "Smith",email: "john@example.com",phone: "+15558675309",job_title: "Developer")company=Attio::Company.create(name: "Acme Corp",domain: "acme.com",employee_count: "50-100")

Raw API Format (Advanced): If you need full control, you can use the raw API structures:

# Namesvalues: {name: [{first_name: "John",last_name: "Smith",full_name: "John Smith"}]}# Phone Numbersvalues: {phone_numbers: [{original_phone_number: "+15558675309",country_code: "US"}]}# Addressesvalues: {primary_location: [{line_1: "1 Infinite Loop",locality: "Cupertino",region: "CA",postcode: "95014",country_code: "US"}]}# Email addresses and domains (simple arrays)values: {email_addresses: ["john@example.com","john.smith@company.com"],domains: ["example.com","example.org"]}

Creating Records

# Create a personperson=Attio::Person.create(first_name: "Jane",last_name: "Smith",email: "jane@example.com",phone: "+1-555-0123",job_title: "CEO")# Create a companycompany=Attio::Company.create(name: "Acme Corp",domain: "acme.com",values: {industry: "Technology"})

Retrieving Records

# Get a specific personperson=Attio::Person.retrieve("rec_456def789")# Access attributes using bracket notationputsperson[:name]putsperson[:email_addresses]putsperson[:job_title]# Note: Attributes can be accessed with bracket notation and symbols

Updating Records

# Update a record using attribute settersperson[:job_title]="CTO"person[:tags]=["vip","customer"]person.save# Or update directlyAttio::Person.update("rec_456def789",values: {job_title: "CTO"})

Searching and Filtering

# Simple searchpeople=Attio::Person.search("john")# Advanced filteringexecutives=Attio::Person.list(params: {filter: {job_title: {"$contains": "CEO"}},sort: [{attribute: "name",direction: "asc"}],limit: 20})# Paginationpage=peoplewhilepage.has_more?page.eachdo |person|
putsperson[:name]endpage=page.next_pageend# Auto-paginationpeople.auto_paging_eachdo |person|
putsperson[:name]end

Deleting Records

# Delete a recordperson.destroy# Or delete by IDAttio::Person.delete("rec_123abc456")# Replace with actual record ID

Note on Batch Operations

The Attio API does not currently support batch operations for creating, updating, or deleting multiple records in a single request. Each record must be processed individually. If you need to process many records, consider implementing rate limiting and error handling in your application.

Convenience Methods

The gem provides many convenience methods to make working with records easier:

Person Methods

person=Attio::Person.retrieve("rec_123")# Access methodsperson.email# Returns primary email addressperson.phone# Returns primary phone number person.first_name# Returns first nameperson.last_name# Returns last nameperson.full_name# Returns full name# Modification methodsperson.set_name(first: "Jane",last: "Doe")person.add_email("jane.doe@example.com")person.add_phone("+14155551234",country_code: "US")# Search methods - Rails-style find_byjane=Attio::Person.find_by(email: "jane@example.com")john=Attio::Person.find_by(name: "John Smith")# Can combine multiple conditions (uses AND logic)exec=Attio::Person.find_by(email: "exec@company.com",job_title: "CEO")

Company Methods

company=Attio::Company.retrieve("rec_456")# Access methodscompany.name# Returns company namecompany.domain# Returns primary domaincompany.domains_list# Returns all domains# Modification methodscompany.name="New Company Name"company.add_domain("newdomain.com")company.add_team_member(person)# Associate a person with the company# Search methods - Rails-style find_byacme=Attio::Company.find_by(name: "Acme Corp")tech_co=Attio::Company.find_by(domain: "techcompany.com")# Can combine multiple conditionsbig_tech=Attio::Company.find_by(domain: "tech.com",employee_count: "100-500")

Deal Methods

# Create a deal (requires name, stage, and owner)deal=Attio::Deal.create(name: "Enterprise Deal",value: 50000,stage: "In Progress",# Options: "Lead", "In Progress", "Won 🎉", "Lost"owner: "sales@company.com"# Must be a workspace member email)# Access methodsdeal.name# Returns deal namedeal.value# Returns currency object with currency_valuedeal.stage# Returns status object with nested titledeal.status# Alias for stagedeal.current_status# Returns the current status title as a stringdeal.status_changed_at# Returns when the status was last changed# Update methodsdeal.update_stage("Won 🎉")deal.update_value(75000)# Search methodsbig_deals=Attio::Deal.find_by_value_range(min: 100000)mid_deals=Attio::Deal.find_by_value_range(min: 50000,max: 100000)won_deals=Attio::Deal.find_by(stage: "Won 🎉")# Query by status using convenience methodswon_deals=Attio::Deal.won# All deals with "Won 🎉" statuslost_deals=Attio::Deal.lost# All deals with "Lost" statusopen_deals=Attio::Deal.open_deals# All deals with "Lead" or "In Progress"# Query by custom stagescustom_deals=Attio::Deal.in_stage(stage_names: ["Won 🎉","Contract Signed"])# Check deal status (uses configuration)deal.open?# true if status is "Lead" or "In Progress"deal.won?# true if status is "Won 🎉"deal.lost?# true if status is "Lost"deal.won_at# timestamp when deal was won (or nil)deal.closed_at# timestamp when deal was closed (won or lost)# Associate with companies and peopledeal=Attio::Deal.create(name: "Partnership Deal",value: 100000,stage: "Lead",owner: "sales@company.com",associated_people: ["contact@partner.com"],associated_company: ["partner.com"]# Uses domain)
Customizing Deal Statuses

The gem uses Attio's default deal statuses ("Lead", "In Progress", "Won 🎉", "Lost") but you can customize these for your workspace:

# In config/initializers/attio.rbAttio.configuredo |config|
config.api_key=ENV["ATTIO_API_KEY"]# Customize which statuses are considered won, lost, or openconfig.won_statuses=["Won 🎉","Contract Signed","Customer"]config.lost_statuses=["Lost","Disqualified","No Budget"]config.open_statuses=["Lead","Qualified Lead","Prospect"]config.in_progress_statuses=["In Progress","Negotiation","Proposal Sent"]end# Now the convenience methods use your custom statuseswon_deals=Attio::Deal.won# Finds deals with any of your won_statusesdeal.won?# Returns true if deal status is in your won_statuses

TypedRecord Methods

All typed records (Person, Company, and custom objects) support:

# Search with query stringresults=Attio::Person.search("john")# Find by any attribute using Rails-style syntaxperson=Attio::Person.find_by(job_title: "CEO")# Or find by multiple attributes (AND logic)person=Attio::Person.find_by(job_title: "CEO",company: "Acme Corp")# Aliases for common methodsAttio::Person.all == Attio::Person.listAttio::Person.find("rec_123") == Attio::Person.retrieve("rec_123")

Lists and List Entries

Lists allow you to organize records into groups.

# Create a listlist=Attio::List.create(name: "VIP Customers",object: "people")# Add records to a listentry=list.add_record("rec_789def012")# Replace with actual record ID# List entriesentries=list.entriesentries.eachdo |entry|
putsentry.record_idend# Remove from list (requires entry_id, not record_id)list.remove_record("ent_456ghi789")# Replace with actual list entry ID# Delete listlist.destroy

Notes

Add notes to records to track interactions and important information.

# Create a notenote=Attio::Note.create(parent_object: "people",parent_record_id: "rec_123abc456",# Replace with actual record IDcontent: "Had a great meeting about the new project.",format: "plaintext"# or "markdown")# List notes for a recordnotes=Attio::Note.list(parent_object: "people",parent_record_id: "rec_123abc456"# Replace with actual record ID)# Notes are immutable - create a new note instead of updating# To "update" a note, you would delete the old one and create a new one# Delete a notenote.destroy

Webhooks

Set up webhooks to receive real-time updates about changes in your workspace.

# Create a webhookwebhook=Attio::Webhook.create(name: "Customer Updates",url: "https://yourapp.com/webhooks/attio",subscriptions: %w[record.createdrecord.updated])# List webhookswebhooks=Attio::Webhook.list# Update webhookwebhook[:active]=falsewebhook.save# Delete webhookwebhook.destroy# Verify webhook signaturesAttio::Util::WebhookSignature.verify!(payload: request.body.read,signature: request.headers['Attio-Signature'],secret: ENV['WEBHOOK_SECRET'])

Advanced Features

OAuth 2.0

Complete OAuth 2.0 flow implementation:

# Initialize clientoauth=Attio::OAuth::Client.new(client_id: ENV['CLIENT_ID'],client_secret: ENV['CLIENT_SECRET'],redirect_uri: "https://yourapp.com/callback")# Authorizationauth_data=oauth.authorization_url(scopes: %w[record:readrecord:writeuser:read],state: SecureRandom.hex(16))# Token exchangetoken=oauth.exchange_code_for_token(code: params[:code],state: params[:state])# Token refreshnew_token=oauth.refresh_token("rtok_xyz789ghi012")# Replace with actual refresh token# Token introspectioninfo=oauth.introspect_token("tok_abc123def456")# Replace with actual access tokenputsinfo[:active]# => true# Token revocationoauth.revoke_token("tok_abc123def456")# Replace with actual access token

Error Handling

The gem provides comprehensive error handling:

beginperson=Attio::Person.create(email: "invalid-email")rescueAttio::InvalidRequestError=>eputs"Validation error: #{e.message}"puts"HTTP status: #{e.code}"puts"Request ID: #{e.request_id}"rescueAttio::AuthenticationError=>eputs"Auth failed: #{e.message}"puts"Request ID: #{e.request_id}"rescueAttio::RateLimitError=>eputs"Rate limited: #{e.message}"rescueAttio::ConnectionError=>eputs"Network error: #{e.message}"rescueAttio::Error=>eputs"API error: #{e.message}"puts"HTTP status: #{e.code}"puts"Request ID: #{e.request_id}"end

Examples

Complete example applications are available in the examples/ directory:

  • basic_usage.rb - Demonstrates core functionality
  • oauth_flow.rb - Complete OAuth 2.0 implementation with Sinatra
  • webhook_server.rb - Webhook handling with signature verification

Run an example:

$ ruby examples/basic_usage.rb

Testing

The gem includes comprehensive test coverage:

# Run all tests (unit tests only by default)
$ bundle exec rspec
# Run unit tests only
$ bundle exec rspec spec/unit
# Run integration tests (requires API key)
$ RUN_INTEGRATION_TESTS=true bundle exec rspec spec/integration

Integration Tests

Note: This gem is under active development. To ensure our implementation matches the Attio API, we leverage live integration tests against a sandbox environment. This strategy will be removed once we hit a stable 1.0 release.

Integration tests make real API calls to Attio and are disabled by default. They serve to:

  • Validate that our WebMock stubs match actual API behavior
  • Test OAuth flows and complex scenarios
  • Ensure the gem works correctly with the latest Attio API

To run integration tests:

  1. Set up your environment variables:

    export ATTIO_API_KEY="your_api_key"export RUN_INTEGRATION_TESTS=true
  2. Run the tests:

    bundle exec rspec spec/integration

Warning: Integration tests will create and delete real data in your Attio workspace. They include automatic cleanup, but use a test workspace if possible.

Unit Tests

Unit tests use WebMock to stub all HTTP requests and do not require an API key. They run by default and ensure the gem's internal logic works correctly.

# Run only unit tests
bundle exec rspec spec/unit
# Run with coverage
$ COVERAGE=true bundle exec rspec

Performance

The gem is optimized for performance:

  • Connection pooling for HTTP keep-alive
  • Automatic retry with exponential backoff
  • Efficient pagination with auto-paging
  • Thread-safe operations

Run benchmarks:

$ ruby benchmarks/api_performance.rb
$ ruby benchmarks/memory_profile.rb

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

The gem is available as open source under the terms of the MIT License.

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages