{"id":31892532,"url":"https://github.com/pablob07/kapso-client-ruby","last_synced_at":"2026-01-20T17:33:45.806Z","repository":{"id":318716588,"uuid":"1073270335","full_name":"PabloB07/kapso-client-ruby","owner":"PabloB07","description":"Unofficial Ruby SDK API for kapso.ai platform","archived":false,"fork":false,"pushed_at":"2025-10-10T06:14:41.000Z","size":76,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-11T12:52:47.983Z","etag":null,"topics":["ai","api","gem","kapso","ruby"],"latest_commit_sha":null,"homepage":"https://kapso.ai","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PabloB07.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-09T21:29:26.000Z","updated_at":"2025-10-10T06:14:44.000Z","dependencies_parsed_at":"2025-10-11T12:52:52.761Z","dependency_job_id":"0895c55d-1b5f-4279-833e-263af4dc47b9","html_url":"https://github.com/PabloB07/kapso-client-ruby","commit_stats":null,"previous_names":["pablob07/kapso-client-ruby"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/PabloB07/kapso-client-ruby","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloB07%2Fkapso-client-ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloB07%2Fkapso-client-ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloB07%2Fkapso-client-ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloB07%2Fkapso-client-ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PabloB07","download_url":"https://codeload.github.com/PabloB07/kapso-client-ruby/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloB07%2Fkapso-client-ruby/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279014289,"owners_count":26085492,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","api","gem","kapso","ruby"],"created_at":"2025-10-13T08:52:45.387Z","updated_at":"2026-01-20T17:33:45.800Z","avatar_url":"https://github.com/PabloB07.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"kapso-ruby-logo.jpg\" alt=\"Kapso Ruby Client\" width=\"400\"\u003e\n\u003c/div\u003e\n\n# Kapso API Ruby SDK\n\n[![Gem Version](https://badge.fury.io/rb/kapso-client-ruby.svg)](https://badge.fury.io/rb/kapso-client-ruby)\n[![Ruby](https://img.shields.io/badge/ruby-%3E%3D%202.7.0-red.svg)](https://www.ruby-lang.org/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nA comprehensive Ruby client library for the [WhatsApp Business Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api/). This SDK provides a complete interface for sending messages, managing media, templates, and more, with built-in error handling, retry logic, and debug capabilities.\n\n## Features\n\n- 🚀 **Complete API Coverage**: All Kapso Cloud API endpoints supported\n- 📱 **Rich Message Types**: Text, media, templates, interactive messages, and more\n- 🔐 **Dual Authentication**: Meta Graph API and Kapso Proxy support\n- 🛡️ **Smart Error Handling**: Comprehensive error categorization and retry logic\n- 📊 **Advanced Features**: Message history, analytics, and contact management (via Kapso)\n- 🔍 **Debug Support**: Detailed logging and request/response tracing\n- 📚 **Type Safety**: Structured response objects and validation\n- ⚡ **Performance**: HTTP connection pooling and efficient request handling\n- 🛤️ **Rails Integration**: First-class Rails support with generators, service classes, and background jobs\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'kapso-client-ruby'\n```\n\nAnd then execute:\n\n```bash\n$ bundle install\n```\n\nOr install it yourself as:\n\n```bash\n$ gem install kapso-client-ruby\n```\n\n### Rails Integration\n\nFor Rails applications, use the built-in generator to set up everything automatically:\n\n```bash\nrails generate kapso_client_ruby:install\n```\n\nThis creates:\n- Configuration initializer\n- Webhook controller\n- Service class for messaging\n- Background job examples\n- Routes for webhooks\n\nSee the [Rails Integration Guide](RAILS_INTEGRATION.md) for detailed Rails-specific documentation.\n\n## Quick Start\n\n### Basic Setup\n\n```ruby\nrequire 'kapso_client_api'\n\n# Initialize client with Meta Graph API access token\nclient = KapsoClientRuby::Client.new(\n  access_token: 'your_access_token'\n)\n\n# Send a text message\nresponse = client.messages.send_text(\n  phone_number_id: 'your_phone_number_id',\n  to: '+1234567890',\n  body: 'Hello from Ruby!'\n)\n\nputs \"Message sent: #{response.messages.first.id}\"\n```\n\n### Using Kapso Proxy (for enhanced features)\n\n```ruby\n# Initialize client with Kapso API key for enhanced features\nkapso_client = KapsoClientRuby::Client.new(\n  kapso_api_key: 'your_kapso_api_key',\n  base_url: 'https://app.kapso.ai/api/meta'\n)\n\n# Access message history and analytics\nmessages = kapso_client.messages.query(\n  phone_number_id: 'your_phone_number_id',\n  direction: 'inbound',\n  limit: 10\n)\n```\n\n## API Reference\n\n### Flows\n\nCreate and manage interactive WhatsApp Flows for rich data collection:\n\n#### Create and Deploy a Flow\n\n```ruby\n# Idempotent deployment (recommended)\ndeployment = client.flows.deploy(\n  business_account_id: 'your_business_id',\n  name: 'appointment_booking',\n  categories: ['APPOINTMENT_BOOKING'],\n  flow_json: {\n    version: '3.0',\n    screens: [\n      {\n        id: 'APPOINTMENT',\n        title: 'Book Appointment',\n        layout: {\n          type: 'SingleColumnLayout',\n          children: [\n            {\n              type: 'Form',\n              name: 'appointment_form',\n              children: [\n                {\n                  type: 'TextInput',\n                  name: 'customer_name',\n                  label: 'Full Name',\n                  required: true\n                },\n                {\n                  type: 'DatePicker',\n                  name: 'appointment_date',\n                  label: 'Preferred Date',\n                  required: true\n                },\n                {\n                  type: 'Footer',\n                  label: 'Submit',\n                  on_click_action: {\n                    name: 'complete',\n                    payload: {\n                      customer_name: '${form.customer_name}',\n                      appointment_date: '${form.appointment_date}'\n                    }\n                  }\n                }\n              ]\n            }\n          ]\n        }\n      }\n    ]\n  }\n)\n\nputs \"Flow ID: #{deployment[:id]}\"\n```\n\n#### Send a Flow Message\n\n```ruby\nrequire 'securerandom'\n\nclient.messages.send_flow(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  flow_id: 'your_flow_id',\n  flow_cta: 'Book Appointment',\n  flow_token: SecureRandom.uuid,\n  header: {\n    type: 'text',\n    text: 'Appointment Booking'\n  },\n  body_text: 'Book your appointment in just a few taps!',\n  footer_text: 'Available slots fill up fast'\n)\n```\n\n#### Handle Flow Webhooks\n\n```ruby\n# In your webhook endpoint\ndef handle_flow_webhook(encrypted_request)\n  private_key = OpenSSL::PKey::RSA.new(File.read('private_key.pem'))\n  \n  # Decrypt incoming Flow event\n  flow_event = client.flows.receive_flow_event(\n    encrypted_request: encrypted_request,\n    private_key: private_key\n  )\n  \n  # Process form data\n  case flow_event.action\n  when 'INIT'\n    response_data = {\n      version: flow_event.version,\n      screen: 'APPOINTMENT',\n      data: { available_dates: ['2024-01-15', '2024-01-16'] }\n    }\n  when 'data_exchange'\n    # Save appointment data\n    customer_name = flow_event.data['customer_name']\n    appointment_date = flow_event.data['appointment_date']\n    \n    response_data = {\n      version: flow_event.version,\n      screen: 'SUCCESS',\n      data: { success: true }\n    }\n  end\n  \n  # Encrypt and return response\n  client.flows.respond_to_flow(\n    response_data: response_data,\n    private_key: private_key\n  )\nend\n```\n\n#### Manage Flows\n\n```ruby\n# List all Flows\nflows = client.flows.list(business_account_id: 'business_id')\n\n# Get Flow details\nflow = client.flows.get(flow_id: 'flow_id')\n\n# Update Flow\nclient.flows.update(\n  flow_id: 'flow_id',\n  categories: ['APPOINTMENT_BOOKING', 'CUSTOMER_SUPPORT']\n)\n\n# Update Flow JSON\nasset_response = client.flows.update_asset(\n  flow_id: 'flow_id',\n  asset: flow_json_hash\n)\n\n# Publish Flow\nclient.flows.publish(flow_id: 'flow_id')\n\n# Get preview URL\npreview = client.flows.preview(flow_id: 'flow_id')\nputs preview.preview_url\n\n# Deprecate Flow\nclient.flows.deprecate(flow_id: 'flow_id')\n\n# Delete Flow\nclient.flows.delete(flow_id: 'flow_id')\n```\n\n### Messages\n\nSend various types of messages with the Messages resource:\n\n#### Text Messages\n\n```ruby\n# Simple text message\nclient.messages.send_text(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  body: 'Hello World!'\n)\n\n# Text with URL preview\nclient.messages.send_text(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  body: 'Check this out: https://example.com',\n  preview_url: true\n)\n```\n\n#### Media Messages\n\n```ruby\n# Send image\nclient.messages.send_image(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  image: {\n    link: 'https://example.com/image.jpg',\n    caption: 'Beautiful sunset'\n  }\n)\n\n# Send document\nclient.messages.send_document(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  document: {\n    id: 'media_id', # or link: 'https://...'\n    filename: 'report.pdf',\n    caption: 'Monthly report'\n  }\n)\n\n# Send audio\nclient.messages.send_audio(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  audio: { id: 'audio_media_id' }\n)\n\n# Send voice note (OGG/OPUS format recommended)\nclient.messages.send_audio(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  audio: { link: 'https://example.com/voice-note.ogg' },\n  voice: true  # Marks as voice note\n)\n\n# Send video\nclient.messages.send_video(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  video: {\n    link: 'https://example.com/video.mp4',\n    caption: 'Tutorial video'\n  }\n)\n```\n\n#### Group Messaging\n\nSend messages to WhatsApp groups by setting `recipient_type: 'group'`:\n\n```ruby\n# Send text to group\nclient.messages.send_text(\n  phone_number_id: 'phone_id',\n  to: '120363XXXXXXXXX@g.us',  # Group ID format\n  body: 'Hello everyone!',\n  recipient_type: 'group'\n)\n\n# Send image to group\nclient.messages.send_image(\n  phone_number_id: 'phone_id',\n  to: '120363XXXXXXXXX@g.us',\n  image: { link: 'https://example.com/team-photo.jpg' },\n  caption: 'Team photo from our event',\n  recipient_type: 'group'\n)\n```\n\n**Note:** Group messaging works with all message types (text, images, videos, documents, interactive messages, etc.)\n\n**Group ID Format:** `XXXXXXXXX@g.us` (WhatsApp group identifier)\n\n#### Location Messages\n\n```ruby\n# Send location\nclient.messages.send_location(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  latitude: 37.7749,\n  longitude: -122.4194,\n  name: 'Our Office',\n  address: '123 Main St, San Francisco, CA'\n)\n\n# Request user's location\nclient.messages.send_interactive_location_request(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  body_text: 'Please share your location for delivery',\n  footer_text: 'Your privacy is important to us'\n)\n\n# Request location with header\nclient.messages.send_interactive_location_request(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  header: {\n    type: 'image',\n    image: { link: 'https://example.com/map-icon.png' }\n  },\n  body_text: 'Share your location to find the nearest store',\n  footer_text: 'Tap to share'\n)\n```\n\n#### Interactive Messages\n\n```ruby\n# Button interactive message\nclient.messages.send_interactive_buttons(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  body_text: 'Choose an option:',\n  buttons: [\n    {\n      type: 'reply',\n      reply: {\n        id: 'option_1',\n        title: 'Option 1'\n      }\n    },\n    {\n      type: 'reply',\n      reply: {\n        id: 'option_2',\n        title: 'Option 2'\n      }\n    }\n  ],\n  header: {\n    type: 'text',\n    text: 'Menu'\n  }\n)\n\n# List interactive message\nclient.messages.send_interactive_list(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  body_text: 'Please select from the list:',\n  button_text: 'View Options',\n  sections: [\n    {\n      title: 'Section 1',\n      rows: [\n        {\n          id: 'item_1',\n          title: 'Item 1',\n          description: 'Description 1'\n        }\n      ]\n    }\n  ]\n)\n```\n\n#### Interactive CTA URL Messages\n\nSend messages with Call-to-Action buttons that open URLs:\n\n```ruby\n# With image header\nclient.messages.send_interactive_cta_url(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  header: {\n    type: 'image',\n    image: { link: 'https://example.com/banner.jpg' }\n  },\n  body_text: 'Get 25% off your first purchase!',\n  display_text: 'Shop Now',  # Max 20 characters\n  url: 'https://shop.example.com?utm_source=whatsapp',\n  footer_text: 'Limited time offer'\n)\n\n# With text header\nclient.messages.send_interactive_cta_url(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  header: {\n    type: 'text',\n    text: 'Special Offer'\n  },\n  body_text: 'Join our exclusive members club!',\n  display_text: 'Join Now',\n  url: 'https://members.example.com/signup'\n)\n\n# With video or document header\nclient.messages.send_interactive_cta_url(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  header: {\n    type: 'video',\n    video: { link: 'https://example.com/demo.mp4' }\n  },\n  body_text: 'Watch our product in action!',\n  display_text: 'Learn More',\n  url: 'https://example.com/product'\n)\n```\n\n**Validations:**\n- `body_text`: Max 1024 characters\n- `display_text`: Max 20 characters\n- `url`: Must be valid HTTP/HTTPS URL\n- `footer_text`: Max 60 characters (optional)\n- `header`: Supports text, image, video, or document\n\n#### Catalog Messages\n\nSend your product catalog directly in WhatsApp:\n\n```ruby\nclient.messages.send_interactive_catalog_message(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  body_text: 'Browse our entire product catalog!',\n  thumbnail_product_retailer_id: 'SKU-001',  # Product SKU for thumbnail\n  footer_text: 'Tap to explore'\n)\n```\n\n**Parameters:**\n- `body_text`: Max 1024 characters\n- `thumbnail_product_retailer_id`: Product SKU to display as thumbnail (required)\n- `footer_text`: Max 60 characters (optional)\n\n#### Template Messages\n\n```ruby\n# Simple template\nclient.messages.send_template(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  name: 'hello_world',\n  language: 'en_US'\n)\n\n# Template with parameters\nclient.messages.send_template(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  name: 'appointment_reminder',\n  language: 'en_US',\n  components: [\n    {\n      type: 'body',\n      parameters: [\n        { type: 'text', text: 'John Doe' },\n        { type: 'text', text: 'Tomorrow at 2 PM' }\n      ]\n    }\n  ]\n)\n```\n\n#### Message Reactions\n\n```ruby\n# Add reaction\nclient.messages.send_reaction(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  message_id: 'message_to_react_to',\n  emoji: '👍'\n)\n\n# Remove reaction\nclient.messages.send_reaction(\n  phone_number_id: 'phone_id',\n  to: '+1234567890',\n  message_id: 'message_to_react_to',\n  emoji: nil\n)\n```\n\n#### Message Status\n\n```ruby\n# Mark message as read\nclient.messages.mark_read(\n  phone_number_id: 'phone_id',\n  message_id: 'message_id'\n)\n\n# Send typing indicator\nclient.messages.send_typing_indicator(\n  phone_number_id: 'phone_id',\n  to: '+1234567890'\n)\n```\n\n### Media Management\n\nHandle media uploads, downloads, and management:\n\n```ruby\n# Upload media\nupload_response = client.media.upload(\n  phone_number_id: 'phone_id',\n  type: 'image',\n  file: '/path/to/image.jpg'\n)\n\nmedia_id = upload_response.id\n\n# Get media metadata\nmetadata = client.media.get(media_id: media_id)\nputs \"File size: #{metadata.file_size} bytes\"\nputs \"MIME type: #{metadata.mime_type}\"\n\n# Download media\ncontent = client.media.download(\n  media_id: media_id,\n  as: :binary\n)\n\n# Save media to file\nclient.media.save_to_file(\n  media_id: media_id,\n  filepath: '/path/to/save/file.jpg'\n)\n\n# Delete media\nclient.media.delete(media_id: media_id)\n```\n\n### Template Management\n\nCreate, manage, and use message templates:\n\n```ruby\n# List templates\ntemplates = client.templates.list(\n  business_account_id: 'your_business_id',\n  status: 'APPROVED'\n)\n\n# Create marketing template\ntemplate_data = client.templates.build_marketing_template(\n  name: 'summer_sale',\n  language: 'en_US',\n  body: 'Hi {{1}}! Our summer sale is here with {{2}} off!',\n  header: {\n    type: 'HEADER',\n    format: 'TEXT',\n    text: 'Summer Sale 🌞'\n  },\n  footer: 'Limited time offer',\n  buttons: [\n    {\n      type: 'URL',\n      text: 'Shop Now',\n      url: 'https://shop.example.com'\n    }\n  ],\n  body_example: {\n    body_text: [['John', '25%']]\n  }\n)\n\nresponse = client.templates.create(\n  business_account_id: 'your_business_id',\n  **template_data\n)\n\n# Create authentication template\nauth_template = client.templates.build_authentication_template(\n  name: 'verify_code',\n  language: 'en_US',\n  ttl_seconds: 300\n)\n\nclient.templates.create(\n  business_account_id: 'your_business_id',\n  **auth_template\n)\n\n# Delete template\nclient.templates.delete(\n  business_account_id: 'your_business_id',\n  name: 'old_template',\n  language: 'en_US'\n)\n```\n\n### Advanced Features (Kapso Proxy)\n\nAccess enhanced features with Kapso proxy:\n\n```ruby\n# Initialize Kapso client\nkapso_client = KapsoClientRuby::Client.new(\n  kapso_api_key: 'your_kapso_key',\n  base_url: 'https://app.kapso.ai/api/meta'\n)\n\n# Message history\nmessages = kapso_client.messages.query(\n  phone_number_id: 'phone_id',\n  direction: 'inbound',\n  since: '2024-01-01T00:00:00Z',\n  limit: 50\n)\n\n# Conversation management\nconversations = kapso_client.conversations.list(\n  phone_number_id: 'phone_id',\n  status: 'active'\n)\n\nconversation = kapso_client.conversations.get(\n  conversation_id: conversations.data.first.id\n)\n\n# Update conversation status\nkapso_client.conversations.update_status(\n  conversation_id: conversation.id,\n  status: 'archived'\n)\n\n# Contact management\ncontacts = kapso_client.contacts.list(\n  phone_number_id: 'phone_id',\n  limit: 100\n)\n\n# Update contact metadata\nkapso_client.contacts.update(\n  phone_number_id: 'phone_id',\n  wa_id: 'contact_wa_id',\n  metadata: {\n    tags: ['premium', 'customer'],\n    source: 'website'\n  }\n)\n\n# Search contacts\nresults = kapso_client.contacts.search(\n  phone_number_id: 'phone_id',\n  query: 'john',\n  search_in: ['profile_name', 'phone_number']\n)\n```\n\n## Configuration\n\n### Global Configuration\n\n```ruby\nKapsoClientRuby.configure do |config|\n  config.debug = true\n  config.timeout = 60\n  config.open_timeout = 10\n  config.max_retries = 3\n  config.retry_delay = 1.0\nend\n```\n\n### Client Configuration\n\n```ruby\nclient = KapsoClientRuby::Client.new(\n  access_token: 'token',\n  debug: true,\n  timeout: 30,\n  logger: Logger.new('whatsapp.log')\n)\n```\n\n### Debug Logging\n\nEnable debug logging to see detailed HTTP requests and responses:\n\n```ruby\n# Enable debug mode\nclient = KapsoClientRuby::Client.new(\n  access_token: 'token',\n  debug: true\n)\n\n# Custom logger\nlogger = Logger.new(STDOUT)\nlogger.level = Logger::DEBUG\n\nclient = KapsoClientRuby::Client.new(\n  access_token: 'token',\n  logger: logger\n)\n```\n\n## Error Handling\n\nThe SDK provides comprehensive error handling with detailed categorization:\n\n```ruby\nbegin\n  client.messages.send_text(\n    phone_number_id: 'phone_id',\n    to: 'invalid_number',\n    body: 'Test'\n  )\nrescue KapsoClientRuby::Errors::GraphApiError =\u003e e\n  puts \"Error: #{e.message}\"\n  puts \"Category: #{e.category}\"\n  puts \"HTTP Status: #{e.http_status}\"\n  puts \"Code: #{e.code}\"\n  \n  # Check error type\n  case e.category\n  when :authorization\n    puts \"Authentication failed - check your access token\"\n  when :parameter\n    puts \"Invalid parameter - check phone number format\"\n  when :throttling\n    puts \"Rate limited - wait before retrying\"\n    if e.retry_hint[:retry_after_ms]\n      sleep(e.retry_hint[:retry_after_ms] / 1000.0)\n    end\n  when :template\n    puts \"Template error - check template name and parameters\"\n  when :media\n    puts \"Media error - check file format and size\"\n  end\n  \n  # Check retry recommendations\n  case e.retry_hint[:action]\n  when :retry\n    puts \"Safe to retry this request\"\n  when :retry_after\n    puts \"Retry after specified delay: #{e.retry_hint[:retry_after_ms]}ms\"\n  when :do_not_retry\n    puts \"Do not retry - permanent error\"\n  when :fix_and_retry\n    puts \"Fix the request and retry\"\n  when :refresh_token\n    puts \"Access token needs to be refreshed\"\n  end\nend\n```\n\n### Error Categories\n\n- `:authorization` - Authentication and token errors\n- `:permission` - Permission and access errors  \n- `:parameter` - Invalid parameters or format errors\n- `:throttling` - Rate limiting errors\n- `:template` - Template-related errors\n- `:media` - Media upload/download errors\n- `:phone_registration` - Phone number registration errors\n- `:integrity` - Message integrity errors\n- `:business_eligibility` - Business account eligibility errors\n- `:reengagement_window` - 24-hour messaging window errors\n- `:waba_config` - WhatsApp Business Account configuration errors\n- `:flow` - WhatsApp Flow errors\n- `:synchronization` - Data synchronization errors\n- `:server` - Server-side errors\n- `:unknown` - Unclassified errors\n\n### Automatic Retry Logic\n\n```ruby\ndef send_with_retry(client, max_retries = 3)\n  retries = 0\n  \n  begin\n    client.messages.send_text(\n      phone_number_id: 'phone_id',\n      to: '+1234567890',\n      body: 'Test message'\n    )\n  rescue KapsoClientRuby::Errors::GraphApiError =\u003e e\n    retries += 1\n    \n    case e.retry_hint[:action]\n    when :retry\n      if retries \u003c= max_retries\n        sleep(retries * 2) # Exponential backoff\n        retry\n      end\n    when :retry_after\n      if e.retry_hint[:retry_after_ms] \u0026\u0026 retries \u003c= max_retries\n        sleep(e.retry_hint[:retry_after_ms] / 1000.0)\n        retry\n      end\n    end\n    \n    raise # Re-raise if no retry\n  end\nend\n```\n\n## Webhook Handling\n\nHandle incoming webhooks from WhatsApp:\n\n```ruby\n# Verify webhook signature\ndef verify_webhook_signature(payload, signature, app_secret)\n  require 'openssl'\n  \n  sig_hash = signature.sub('sha256=', '')\n  expected_sig = OpenSSL::HMAC.hexdigest('sha256', app_secret, payload)\n  \n  sig_hash == expected_sig\nend\n\n# In your webhook endpoint\ndef handle_webhook(request)\n  payload = request.body.read\n  signature = request.headers['X-Hub-Signature-256']\n  \n  unless verify_webhook_signature(payload, signature, ENV['WHATSAPP_APP_SECRET'])\n    return [401, {}, ['Unauthorized']]\n  end\n  \n  webhook_data = JSON.parse(payload)\n  \n  # Process webhook data\n  webhook_data['entry'].each do |entry|\n    entry['changes'].each do |change|\n      if change['field'] == 'messages'\n        messages = change['value']['messages'] || []\n        messages.each do |message|\n          handle_incoming_message(message)\n        end\n      end\n    end\n  end\n  \n  [200, {}, ['OK']]\nend\n\ndef handle_incoming_message(message)\n  case message['type']\n  when 'text'\n    puts \"Received text: #{message['text']['body']}\"\n  when 'image'\n    puts \"Received image: #{message['image']['id']}\"\n  when 'interactive'\n    puts \"Received interactive response: #{message['interactive']}\"\n  end\nend\n```\n\n## Testing\n\nRun the test suite:\n\n```bash\n# Install development dependencies\nbundle install\n\n# Run tests\nbundle exec rspec\n\n# Run tests with coverage\nbundle exec rspec --format documentation\n\n# Run rubocop for style checking\nbundle exec rubocop\n```\n\n### Testing with VCR\n\nThe SDK includes VCR cassettes for testing without making real API calls:\n\n```ruby\n# spec/spec_helper.rb\nrequire 'vcr'\n\nVCR.configure do |config|\n  config.cassette_library_dir = 'spec/vcr_cassettes'\n  config.hook_into :webmock\n  config.configure_rspec_metadata!\n  \n  # Filter sensitive data\n  config.filter_sensitive_data('\u003cACCESS_TOKEN\u003e') { ENV['WHATSAPP_ACCESS_TOKEN'] }\n  config.filter_sensitive_data('\u003cPHONE_NUMBER_ID\u003e') { ENV['PHONE_NUMBER_ID'] }\nend\n\n# In your tests\nRSpec.describe 'Messages' do\n  it 'sends text message', :vcr do\n    client = KapsoClientRuby::Client.new(access_token: 'test_token')\n    \n    response = client.messages.send_text(\n      phone_number_id: 'test_phone_id',\n      to: '+1234567890',\n      body: 'Test message'\n    )\n    \n    expect(response.messages.first.id).to be_present\n  end\nend\n```\n\n## Examples\n\nSee the [examples](examples/) directory for comprehensive usage examples:\n\n- [Basic Messaging](examples/basic_messaging.rb) - Text, media, and template messages\n- [Media Management](examples/media_management.rb) - Upload, download, and manage media\n- [Template Management](examples/template_management.rb) - Create and manage templates\n- [Advanced Features](examples/advanced_features.rb) - Kapso proxy features and analytics\n\n## Requirements\n\n- Ruby \u003e= 2.7.0\n- Faraday \u003e= 2.0\n- A WhatsApp Business Account with Cloud API access\n- Valid access token from Meta or Kapso API key\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/gokapso/whatsapp-cloud-api-ruby.\n\n### Development Setup\n\n```bash\ngit clone https://github.com/gokapso/whatsapp-cloud-api-ruby.git\ncd whatsapp-cloud-api-ruby\nbundle install\n```\n\n### Running Tests\n\n```bash\n# Run all tests\nbundle exec rspec\n\n# Run specific test file\nbundle exec rspec spec/client_spec.rb\n\n# Run with coverage\nCOVERAGE=true bundle exec rspec\n```\n\n### Code Style\n\n```bash\n# Check style\nbundle exec rubocop\n\n# Auto-fix issues\nbundle exec rubocop -A\n```\n\n## License\n\nThis gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Support\n\n- 📖 [WhatsApp Cloud API Documentation](https://developers.facebook.com/docs/whatsapp/cloud-api/)\n- 🌐 [Kapso Platform](https://kapso.ai/) for enhanced features\n- 🐛 [Issue Tracker](https://github.com/PabloB07/kapso-client-ruby/issues)\n- 📧 Email: support@kapso.ai\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for version history and updates.\n\n---\n\nBuilt with ❤️ for the [Kapso](https://kapso.ai) team","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpablob07%2Fkapso-client-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpablob07%2Fkapso-client-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpablob07%2Fkapso-client-ruby/lists"}