From cb588057a58a091885b0e299dd95826c50f06b76 Mon Sep 17 00:00:00 2001 From: drish Date: Fri, 3 Jul 2026 18:01:02 -0300 Subject: [PATCH] feat: add domain claims endpoints Adds Domains::Claims module with create, get, and verify methods covering the new Domain Claims API: - POST /domains/claim - GET /domains/{domain_id}/claim - POST /domains/{domain_id}/claim/verify --- examples/domain_claims.rb | 30 +++++++++++ lib/resend.rb | 1 + lib/resend/domains/claims.rb | 56 +++++++++++++++++++ spec/domain_claims_spec.rb | 101 +++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 examples/domain_claims.rb create mode 100644 lib/resend/domains/claims.rb create mode 100644 spec/domain_claims_spec.rb diff --git a/examples/domain_claims.rb b/examples/domain_claims.rb new file mode 100644 index 0000000..1f0c9af --- /dev/null +++ b/examples/domain_claims.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require_relative "../lib/resend" + +raise if ENV["RESEND_API_KEY"].nil? + +Resend.api_key = ENV["RESEND_API_KEY"] + +def claim_domain + params = { + name: "example.com", + region: "us-east-1" + } + claim = Resend::Domains::Claims.create(params) + puts "Created domain claim: #{claim[:id]}" + puts "Status: #{claim[:status]}" + + record = claim[:record] + puts "Add this TXT record to prove ownership: #{record[:name]} = #{record[:value]}" if record + + # Get: poll the claim until the TXT record has been added and verification can run. + retrieved = Resend::Domains::Claims.get(claim[:domain_id]) + puts "Retrieved domain claim status: #{retrieved[:status]}" + + # Verify: trigger asynchronous DNS verification and ownership transfer. + verified = Resend::Domains::Claims.verify(claim[:domain_id]) + puts "Verification triggered, claim status: #{verified[:status]}" +end + +claim_domain diff --git a/lib/resend.rb b/lib/resend.rb index d71388b..37a2c09 100644 --- a/lib/resend.rb +++ b/lib/resend.rb @@ -25,6 +25,7 @@ require "resend/contacts/topics" require "resend/contact_properties" require "resend/domains" +require "resend/domains/claims" require "resend/emails" require "resend/templates" require "resend/emails/receiving" diff --git a/lib/resend/domains/claims.rb b/lib/resend/domains/claims.rb new file mode 100644 index 0000000..6e9a43f --- /dev/null +++ b/lib/resend/domains/claims.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Resend + module Domains + # Domain Claims API wrapper + module Claims + class << self + # + # Start a claim for a domain that another Resend account has already verified. + # The domain is recreated under your account with fresh DKIM keys, so the + # previous account's DNS records cannot be reused. Returns a TXT record to add + # to your DNS to prove ownership. Uses the same request body as creating a domain. + # + # @param params [Hash] the parameters + # @option params [String] :name the name of the domain you want to claim (required) + # @option params [String] :region the region where emails will be sent from. + # Possible values: 'us-east-1' | 'eu-west-1' | 'sa-east-1' | 'ap-northeast-1' + # @option params [String] :custom_return_path subdomain for the Return-Path address (default 'send') + # @option params [String] :tracking_subdomain the custom subdomain used for click and open tracking links + # @option params [Boolean] :click_tracking track clicks within the body of each HTML email + # @option params [Boolean] :open_tracking track the open rate of each email + # + # https://resend.com/docs/api-reference/domains/claim-domain + def create(params) + path = "domains/claim" + Resend::Request.new(path, params, "post").perform + end + + # + # Retrieve the latest claim for the placeholder domain created by the claim. + # + # @param domain_id [String] the ID of the placeholder domain created by the claim + # + # https://resend.com/docs/api-reference/domains/get-domain-claim + def get(domain_id = "") + path = "domains/#{domain_id}/claim" + Resend::Request.new(path, {}, "get").perform + end + + # + # Trigger asynchronous DNS verification and ownership transfer for a domain claim. + # The claim stays 'pending' while verification runs; poll `get` for status. Once + # 'completed', the transferred domain has new DKIM records that must be added to + # DNS and verified via Resend::Domains.verify. + # + # @param domain_id [String] the ID of the placeholder domain created by the claim + # + # https://resend.com/docs/api-reference/domains/verify-domain-claim + def verify(domain_id = "") + path = "domains/#{domain_id}/claim/verify" + Resend::Request.new(path, {}, "post").perform + end + end + end + end +end diff --git a/spec/domain_claims_spec.rb b/spec/domain_claims_spec.rb new file mode 100644 index 0000000..7d1a552 --- /dev/null +++ b/spec/domain_claims_spec.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +RSpec.describe "Domains::Claims" do + before do + Resend.configure do |config| + config.api_key = "re_123" + end + end + + describe "create" do + it "should start a domain claim" do + resp = { + object: "domain_claim", + id: "dacf4072-4119-4d88-932f-6c6126d3a9d1", + name: "example.com", + status: "pending", + domain_id: "d91cd9bd-1176-453e-8fc1-35364d380206", + region: "us-east-1", + record: { + type: "TXT", + name: "example.com", + value: "resend-domain-verification=3f8a1c2d4e5b6a7f8091a2b3c4d5e6f7", + ttl: "Auto" + }, + blocked_reason: nil, + failure_reason: nil, + created_at: "2026-06-16T17:12:02.059593+00:00", + expires_at: "2026-06-23T17:12:02.059593+00:00" + } + + params = { name: "example.com" } + allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp) + claim = Resend::Domains::Claims.create(params) + expect(claim[:id]).to eql("dacf4072-4119-4d88-932f-6c6126d3a9d1") + expect(claim[:object]).to eql("domain_claim") + expect(claim[:status]).to eql("pending") + expect(claim[:domain_id]).to eql("d91cd9bd-1176-453e-8fc1-35364d380206") + expect(claim[:record][:type]).to eql("TXT") + expect(claim[:record][:value]).to eql("resend-domain-verification=3f8a1c2d4e5b6a7f8091a2b3c4d5e6f7") + end + + it "should pass all options in the request body" do + resp = { object: "domain_claim", id: "dacf4072-4119-4d88-932f-6c6126d3a9d1", status: "pending" } + + params = { + name: "example.com", + region: "us-east-1", + custom_return_path: "send", + open_tracking: true, + click_tracking: false, + tracking_subdomain: "links" + } + + expect(Resend::Request).to receive(:new).with("domains/claim", params, "post").and_call_original + allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp) + Resend::Domains::Claims.create(params) + end + end + + describe "get" do + it "should retrieve a domain claim" do + resp = { + object: "domain_claim", + id: "dacf4072-4119-4d88-932f-6c6126d3a9d1", + name: "example.com", + status: "blocked", + domain_id: "d91cd9bd-1176-453e-8fc1-35364d380206", + region: "us-east-1", + blocked_reason: "grace_period", + failure_reason: nil, + created_at: "2026-06-16T17:12:02.059593+00:00", + expires_at: "2026-06-23T17:12:02.059593+00:00" + } + allow(resp).to receive(:body).and_return(resp) + allow(HTTParty).to receive(:send).and_return(resp) + + claim = Resend::Domains::Claims.get(resp[:domain_id]) + + expect(claim[:id]).to eql("dacf4072-4119-4d88-932f-6c6126d3a9d1") + expect(claim[:status]).to eql("blocked") + expect(claim[:blocked_reason]).to eql("grace_period") + end + end + + describe "verify" do + it "should trigger verification for a domain claim" do + resp = { + object: "domain_claim", + id: "dacf4072-4119-4d88-932f-6c6126d3a9d1", + name: "example.com", + status: "pending", + domain_id: "d91cd9bd-1176-453e-8fc1-35364d380206", + region: "us-east-1" + } + allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp) + claim = Resend::Domains::Claims.verify("d91cd9bd-1176-453e-8fc1-35364d380206") + expect(claim[:id]).to eql("dacf4072-4119-4d88-932f-6c6126d3a9d1") + expect(claim[:status]).to eql("pending") + end + end +end