Project

nahook

0.0
The project is in a healthy, maintained state
Ruby client for sending webhooks and managing resources through the Nahook API. Supports direct endpoint delivery, fan-out by event type, batch operations, and full management API access.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 2.0
 Project Readme

Nahook Ruby SDK

Official Ruby SDK for the Nahook webhook platform. Send webhooks, fan-out by event type, and manage resources programmatically.

Installation

Add to your Gemfile:

gem "nahook"

Or install directly:

gem install nahook

Requirements: Ruby 3.0+

Quick Start

Sending Webhooks (Client)

require "nahook"

client = Nahook::Client.new("nhk_us_your_api_key")

# Send to a specific endpoint
result = client.send("ep_abc123", payload: { order_id: "12345", status: "paid" })
puts result["deliveryId"] # => "del_..."

# Fan-out by event type (delivers to all subscribed endpoints)
result = client.trigger("order.paid", payload: { order_id: "12345" })
puts result["deliveryIds"] # => ["del_1", "del_2"]

# With metadata
client.trigger("order.paid",
  payload: { order_id: "12345" },
  metadata: { "source" => "checkout" }
)

Managing Resources (Management)

mgmt = Nahook::Management.new("nhm_your_management_token")

# Endpoints
endpoints = mgmt.endpoints.list("ws_abc123")
endpoint  = mgmt.endpoints.create("ws_abc123",
  url: "https://example.com/webhook",
  type: "webhook",
  description: "Production webhook"
)
mgmt.endpoints.update("ws_abc123", endpoint["id"], is_active: false)
mgmt.endpoints.delete("ws_abc123", endpoint["id"])

# Event Types
mgmt.event_types.create("ws_abc123", name: "order.paid", description: "Fired when an order is paid")
types = mgmt.event_types.list("ws_abc123")

# Applications
app = mgmt.applications.create("ws_abc123", name: "Acme Corp", external_id: "acme_123")
mgmt.applications.list("ws_abc123", limit: 10, offset: 0)
mgmt.applications.list_endpoints("ws_abc123", app["id"])
mgmt.applications.create_endpoint("ws_abc123", app["id"], url: "https://acme.com/hook")

# Subscriptions
mgmt.subscriptions.create("ws_abc123", "ep_def456", event_type_ids: ["evt_ghi789"])
mgmt.subscriptions.list("ws_abc123", "ep_def456")
mgmt.subscriptions.delete("ws_abc123", "ep_def456", "evt_ghi789")

# Environments
env = mgmt.environments.create("ws_abc123", name: "Staging", slug: "staging")
mgmt.environments.list("ws_abc123")
mgmt.environments.get("ws_abc123", env["id"])
mgmt.environments.update("ws_abc123", env["id"], name: "Pre-production")
mgmt.environments.delete("ws_abc123", env["id"])

# Event Type Visibility
mgmt.environments.list_event_type_visibility("ws_abc123", "env_abc123")
mgmt.environments.set_event_type_visibility("ws_abc123", "env_abc123", "evt_abc123", published: true)

# Portal Sessions
session = mgmt.portal_sessions.create("ws_abc123", "app_jkl012")
puts session["url"] # Redirect your customer here

Client Options

Nahook::Client

client = Nahook::Client.new("nhk_us_...",
  timeout_ms: 30_000,                   # milliseconds, default
  retries: 3                            # retry on 5xx/429/network errors
)

Configuration

The SDK automatically routes requests to the correct regional API based on your API key prefix (nhk_us_... -> US, nhk_eu_... -> EU, nhk_ap_... -> Asia Pacific). No configuration needed.

To override the base URL (for testing or local development):

client = Nahook::Client.new("nhk_us_...", base_url: "http://localhost:3001")

For unit tests, mock the SDK client at the dependency injection boundary. For integration tests, override the base URL to point at a local server.

Nahook::Management

mgmt = Nahook::Management.new("nhm_...",
  timeout_ms: 30_000                    # milliseconds, default
)
# Note: Management does not support retries

Batch Operations

# Send to multiple endpoints (max 20)
result = client.send_batch([
  { endpoint_id: "ep_abc", payload: { order: 1 } },
  { endpoint_id: "ep_def", payload: { order: 2 }, idempotency_key: "key-2" }
])

# Fan-out multiple event types (max 20)
result = client.trigger_batch([
  { event_type: "order.paid", payload: { order_id: "123" } },
  { event_type: "user.created", payload: { user_id: "456" }, metadata: { "source" => "api" } }
])

Idempotency

The send method auto-generates a UUID idempotency key if you don't provide one:

# Auto-generated idempotency key
client.send("ep_abc", payload: { order: 1 })

# Explicit idempotency key
client.send("ep_abc", payload: { order: 1 }, idempotency_key: "order-1-v1")

Error Handling

begin
  client.send("ep_abc", payload: { test: true })
rescue Nahook::APIError => e
  puts e.message       # Human-readable message
  puts e.status        # HTTP status code
  puts e.code          # Machine-readable error code
  puts e.retryable?    # true for 5xx and 429
  puts e.auth_error?   # true for 401, or 403 with token_disabled
  puts e.not_found?    # true for 404
  puts e.rate_limited? # true for 429
  puts e.retry_after   # Retry-After header value (seconds), if present
rescue Nahook::NetworkError => e
  puts e.message       # "Network error: ..."
  puts e.original_error # Original exception
rescue Nahook::TimeoutError => e
  puts e.message       # "Request timed out after 30000ms"
  puts e.timeout_ms    # Timeout in milliseconds
rescue Nahook::Error => e
  # Catch-all for any SDK error
end

Retry Logic

When retries is configured on Nahook::Client, the SDK automatically retries on:

  • HTTP 5xx responses
  • HTTP 429 (rate limited) -- respects Retry-After header
  • Network connection failures
  • Request timeouts

Retry delay uses exponential backoff with full jitter (base 500ms, max 10s).

License

MIT