class Attio::Webhook

Represents a webhook configuration in Attio

def self.resource_path

Returns:
  • (String) - The API path
def self.resource_path
  "webhooks"
end

def active?

Check if webhook is active
def active?
  active == true
end

def create(**kwargs)

Override create to handle keyword arguments
def create(**kwargs)
  opts = {}
  opts[:api_key] = kwargs.delete(:api_key) if kwargs.key?(:api_key)
  prepared_params = prepare_params_for_create(kwargs)
  response = execute_request(:POST, resource_path, prepared_params, opts)
  new(response["data"] || response, opts)
end

def delete(id, **opts)

Override delete to handle hash IDs
def delete(id, **opts)
  webhook_id = Util::IdExtractor.extract_for_resource(id, :webhook)
  execute_request(:DELETE, "#{resource_path}/#{webhook_id}", {}, opts)
  true
end

def deliveries(params = {}, **opts)

Get recent deliveries for this webhook
def deliveries(params = {}, **opts)
  raise InvalidRequestError, "Cannot get deliveries for a webhook without an ID" unless persisted?
  response = self.class.send(:execute_request, :GET, "#{resource_path}/deliveries", params, opts)
  response[:data] || []
end

def destroy(**opts)

Override destroy to handle nested ID
def destroy(**opts)
  raise InvalidRequestError, "Cannot destroy a webhook without an ID" unless persisted?
  webhook_id = Util::IdExtractor.extract_for_resource(id, :webhook)
  self.class.delete(webhook_id, **opts)
  freeze
  true
end

def initialize(attributes = {}, opts = {})

def initialize(attributes = {}, opts = {})
  super
  normalized_attrs = normalize_attributes(attributes)
  @secret = normalized_attrs[:secret]
  @last_event_at = parse_timestamp(normalized_attrs[:last_event_at])
  @created_by_actor = normalized_attrs[:created_by_actor]
  # Map status to active for convenience
  if status == "active"
    instance_variable_set(:@active, true)
  elsif status == "paused"
    instance_variable_set(:@active, false)
  end
end

def pause(**opts)

Pause the webhook
def pause(**opts)
  self.active = false
  save(**opts)
end

def paused?

Check if webhook is paused
def paused?
  !active?
end

def prepare_params_for_create(params)

Override create to handle validation
def prepare_params_for_create(params)
  # Handle both url and target_url parameters for convenience
  target_url = params[:target_url] || params["target_url"] || params[:url] || params["url"]
  validate_target_url!(target_url)
  subscriptions = params[:subscriptions] || params["subscriptions"]
  validate_subscriptions!(subscriptions)
  {
    data: {
      target_url: target_url,
      subscriptions: Array(subscriptions).map do |sub|
        # Ensure each subscription has a filter
        sub = sub.is_a?(Hash) ? sub : {"event_type" => sub}
        sub["filter"] ||= {"$and" => []}  # Default empty filter
        sub
      end
    }
  }
end

def prepare_params_for_update(params)

Override update params preparation
def prepare_params_for_update(params)
  {
    data: params
  }
end

def resource_path

def resource_path
  raise InvalidRequestError, "Cannot generate path without an ID" unless persisted?
  webhook_id = Util::IdExtractor.extract_for_resource(id, :webhook)
  "#{self.class.resource_path}/#{webhook_id}"
end

def resume(**opts)

Resume the webhook
def resume(**opts)
  self.active = true
  save(**opts)
end

def retrieve(id, **opts)

Override retrieve to handle hash IDs
def retrieve(id, **opts)
  webhook_id = Util::IdExtractor.extract_for_resource(id, :webhook)
  response = execute_request(:GET, "#{resource_path}/#{webhook_id}", {}, opts)
  new(response["data"] || response, opts)
end

def save(**)

Override save to handle nested ID
def save(**)
  raise InvalidRequestError, "Cannot save a webhook without an ID" unless persisted?
  return self unless changed?
  webhook_id = Util::IdExtractor.extract_for_resource(id, :webhook)
  self.class.update(webhook_id, changed_attributes, **)
end

def test(**opts)

Test the webhook with a sample payload
def test(**opts)
  raise InvalidRequestError, "Cannot test a webhook without an ID" unless persisted?
  self.class.send(:execute_request, :POST, "#{resource_path}/test", {}, opts)
  true
end

def to_h

Returns:
  • (Hash) - Webhook data as a hash
def to_h
  super.merge(
    target_url: target_url,
    subscriptions: subscriptions,
    status: status,
    secret: secret,
    last_event_at: last_event_at&.iso8601,
    created_by_actor: created_by_actor
  ).compact
end

def validate_subscriptions!(subscriptions)

def validate_subscriptions!(subscriptions)
  raise ArgumentError, "subscriptions are required" if subscriptions.nil? || subscriptions.empty?
  raise ArgumentError, "subscriptions must be an array" unless subscriptions.is_a?(Array)
  subscriptions.each do |sub|
    event_type = if sub.is_a?(Hash)
      sub[:event_type] || sub["event_type"]
    else
      sub  # sub is a string representing the event type
    end
    raise ArgumentError, "Each subscription must have an event_type" unless event_type
  end
end

def validate_target_url!(url)

def validate_target_url!(url)
  raise BadRequestError, "target_url or url is required" if url.nil? || url.empty?
  uri = URI.parse(url)
  unless uri.scheme == "https"
    raise BadRequestError, "Webhook target_url must use HTTPS"
  end
rescue URI::InvalidURIError
  raise BadRequestError, "Invalid webhook target_url"
end