Ruby SDK
An idiomatic Ruby client for the IdentityCall API with Rails integration support.
Installation
gem install httparty multipart-post
# Or add to Gemfile
gem 'httparty'
gem 'multipart-post'Quick Start
require_relative 'identitycall'
client = IdentityCall::Client.new
# List recordings
result = client.recordings.list
result['data'].each do |recording|
puts "#{recording['id']}: #{recording['name']}"
endComplete Client Implementation
Create a file called identitycall.rb:
identitycall.rb
require 'net/http'
require 'net/http/post/multipart'
require 'json'
require 'uri'
module IdentityCall
# Base exception class
class Error < StandardError
attr_reader :status_code, :response
def initialize(message, status_code: nil, response: nil)
@status_code = status_code
@response = response
super(message)
end
end
class AuthenticationError < Error; end
class PermissionError < Error; end
class NotFoundError < Error; end
class ValidationError < Error; end
class RateLimitError < Error
attr_reader :retry_after
def initialize(message, retry_after: 60, **kwargs)
@retry_after = retry_after
super(message, **kwargs)
end
end
# Recordings resource
class Recordings
def initialize(client)
@client = client
end
def list(page: 1, per_page: 20)
@client.request(:get, '/recordings', params: { page: page, per_page: per_page })
end
def create(file_path, language: 'en', name: nil)
@client.upload('/recordings', file_path, language: language, name: name)
end
def get(recording_id)
@client.request(:get, "/recordings/#{recording_id}")
end
def update(recording_id, **data)
@client.request(:patch, "/recordings/#{recording_id}", json: data)
end
def delete(recording_id)
@client.request(:delete, "/recordings/#{recording_id}")
end
def transcription(recording_id)
@client.request(:get, "/recordings/#{recording_id}/transcription")
end
def results(recording_id)
@client.request(:get, "/recordings/#{recording_id}/results")
end
def summary(recording_id)
@client.request(:get, "/recordings/#{recording_id}/summary")
end
def wait_for_completion(recording_id, timeout: 600, poll_interval: 5)
start_time = Time.now
loop do
result = get(recording_id)
recording = result['data']
return recording if recording['status'] == 'completed'
raise Error.new('Transcription failed') if recording['status'] == 'failed'
raise Timeout::Error, 'Transcription timed out' if Time.now - start_time > timeout
sleep(poll_interval)
end
end
end
# Main client
class Client
DEFAULT_BASE_URL = 'https://api.identitycall.com/api/v1/public'.freeze
MAX_RETRIES = 3
attr_reader :api_key, :base_url, :recordings
def initialize(api_key: nil, base_url: nil, timeout: 30)
@api_key = api_key || ENV['IDENTITYCALL_API_KEY']
raise ArgumentError, 'API key is required' unless @api_key
@base_url = base_url || DEFAULT_BASE_URL
@timeout = timeout
@recordings = Recordings.new(self)
end
def request(method, path, params: nil, json: nil)
uri = build_uri(path, params)
http = build_http(uri)
request = build_request(method, uri, json)
with_retry do
response = http.request(request)
handle_response(response)
end
end
def upload(path, file_path, language:, name: nil)
uri = build_uri(path)
http = build_http(uri)
File.open(file_path) do |file|
params = {
'file' => UploadIO.new(file, 'audio/mpeg', File.basename(file_path)),
'language' => language
}
params['name'] = name if name
request = Net::HTTP::Post::Multipart.new(uri.path, params)
request['Authorization'] = "Bearer #{@api_key}"
request['Accept'] = 'application/json'
with_retry do
response = http.request(request)
handle_response(response)
end
end
end
private
def build_uri(path, params = nil)
uri = URI.parse("#{@base_url}#{path}")
uri.query = URI.encode_www_form(params) if params
uri
end
def build_http(uri)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = @timeout
http.open_timeout = @timeout
http
end
def build_request(method, uri, json = nil)
request_class = {
get: Net::HTTP::Get,
post: Net::HTTP::Post,
patch: Net::HTTP::Patch,
delete: Net::HTTP::Delete
}[method]
request = request_class.new(uri)
request['Authorization'] = "Bearer #{@api_key}"
request['Accept'] = 'application/json'
if json
request['Content-Type'] = 'application/json'
request.body = JSON.generate(json)
end
request
end
def with_retry
attempts = 0
begin
attempts += 1
yield
rescue RateLimitError => e
if attempts < MAX_RETRIES
sleep(e.retry_after)
retry
end
raise
rescue Error => e
if e.status_code && e.status_code >= 500 && attempts < MAX_RETRIES
sleep(2**attempts)
retry
end
raise
end
end
def handle_response(response)
return nil if response.code == '204'
data = response.body.to_s.empty? ? {} : JSON.parse(response.body)
return data if response.is_a?(Net::HTTPSuccess)
error_message = data['error'] || 'Unknown error'
status_code = response.code.to_i
raise_error(status_code, error_message, data)
end
def raise_error(status_code, message, data)
case status_code
when 401
raise AuthenticationError.new(message, status_code: status_code, response: data)
when 403
raise PermissionError.new(message, status_code: status_code, response: data)
when 404
raise NotFoundError.new(message, status_code: status_code, response: data)
when 422
raise ValidationError.new(message, status_code: status_code, response: data)
when 429
retry_after = data['retry_after'] || 60
raise RateLimitError.new(message, retry_after: retry_after, status_code: status_code, response: data)
else
raise Error.new(message, status_code: status_code, response: data)
end
end
end
endUsage Examples
List Recordings
require_relative 'identitycall'
client = IdentityCall::Client.new
# Get first page
result = client.recordings.list
result['data'].each do |recording|
puts "[#{recording['id']}] #{recording['name']} - #{recording['status']}"
end
# With pagination
result = client.recordings.list(page: 2, per_page: 50)
meta = result['meta']
puts "Page #{meta['current_page']} of #{meta['total_pages']}"Upload Recording
result = client.recordings.create(
'/path/to/call.mp3',
language: 'en',
name: 'Customer Support Call'
)
recording_id = result['data']['id']
puts "Uploaded! Recording ID: #{recording_id}"Wait for Transcription
# Upload
result = client.recordings.create('/path/to/call.mp3')
recording_id = result['data']['id']
# Wait for completion
puts 'Processing...'
recording = client.recordings.wait_for_completion(recording_id)
puts "Completed! Duration: #{recording['duration_ms']}ms"
# Get transcription
transcription = client.recordings.transcription(recording_id)
puts transcription['data']['full_text']Get Analysis Results
results = client.recordings.results(recording_id)
# Goals
puts 'Goals:'
results['data']['goals'].each do |goal|
status = goal['met'] ? '✓' : '✗'
puts " #{status} #{goal['goal_name']}: #{format('%.2f', goal['score'])}"
end
# Keywords
puts "\nKeywords:"
results['data']['keywords'].each do |kw|
puts " \"#{kw['keyword_name']}\" - #{kw['count']} mentions"
endError Handling
require_relative 'identitycall'
client = IdentityCall::Client.new
begin
recording = client.recordings.get(999_999)
rescue IdentityCall::NotFoundError
puts 'Recording not found'
rescue IdentityCall::PermissionError
puts 'Permission denied'
rescue IdentityCall::AuthenticationError
puts 'Invalid API key'
rescue IdentityCall::RateLimitError => e
puts "Rate limited. Retry in #{e.retry_after} seconds"
endIterate All Pages
def get_all_recordings(client)
all_recordings = []
page = 1
per_page = 100
loop do
result = client.recordings.list(page: page, per_page: per_page)
all_recordings.concat(result['data'])
meta = result['meta']
break if meta['current_page'] >= meta['total_pages']
page += 1
end
all_recordings
end
# Usage
recordings = get_all_recordings(client)
puts "Total recordings: #{recordings.length}"Enumerator Pattern
def each_recording(client, per_page: 100)
return enum_for(:each_recording, client, per_page: per_page) unless block_given?
page = 1
loop do
result = client.recordings.list(page: page, per_page: per_page)
result['data'].each { |recording| yield recording }
meta = result['meta']
break if meta['current_page'] >= meta['total_pages']
page += 1
end
end
# Usage
each_recording(client).each do |recording|
puts recording['name']
end
# Or with Enumerator methods
each_recording(client).take(10).each { |r| process(r) }Rails Integration
Initializer
config/initializers/identitycall.rb
require_relative '../../lib/identitycall'
Rails.application.config.identitycall = IdentityCall::Client.new(
api_key: Rails.application.credentials.identitycall_api_key,
base_url: ENV.fetch('IDENTITYCALL_BASE_URL', 'https://api.identitycall.com/api/v1/public')
)Helper Method
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
private
def identitycall
Rails.application.config.identitycall
end
endController Usage
app/controllers/recordings_controller.rb
class RecordingsController < ApplicationController
def index
result = identitycall.recordings.list(page: params[:page] || 1)
@recordings = result['data']
@meta = result['meta']
end
def show
result = identitycall.recordings.get(params[:id])
@recording = result['data']
end
def create
if params[:file].present?
result = identitycall.recordings.create(
params[:file].tempfile.path,
language: params[:language] || 'en',
name: params[:name]
)
redirect_to recording_path(result['data']['id']), notice: 'Recording uploaded!'
else
redirect_to new_recording_path, alert: 'Please select a file'
end
end
endBackground Job
app/jobs/process_recording_job.rb
class ProcessRecordingJob < ApplicationJob
queue_as :default
def perform(recording_id)
client = Rails.application.config.identitycall
# Wait for transcription
recording = client.recordings.wait_for_completion(recording_id, timeout: 600)
# Get results
results = client.recordings.results(recording_id)
transcription = client.recordings.transcription(recording_id)
# Store or process results
RecordingResult.create!(
external_id: recording_id,
duration_ms: recording['duration_ms'],
full_text: transcription['data']['full_text'],
goals_met: results['data']['goals'].count { |g| g['met'] },
goals_total: results['data']['goals'].count
)
end
endGem Structure
For a full gem implementation:
identitycall-ruby/
├── lib/
│ ├── identitycall.rb
│ ├── identitycall/
│ │ ├── client.rb
│ │ ├── resources/
│ │ │ └── recordings.rb
│ │ ├── errors.rb
│ │ └── version.rb
├── identitycall.gemspec
├── Gemfile
└── README.md