Add Domain Claims API support (claim + get + verify) - #133
Conversation
Implements the three Domain Claims endpoints:
- POST /domains/claim -> DomainClaimAsync
- GET /domains/{id}/claim -> DomainClaimRetrieveAsync
- POST /domains/{id}/claim/verify -> DomainClaimVerifyAsync
Adds DomainClaim / DomainClaimRecord / DomainClaimData models and a
DomainClaimStatus enum, a "domain claim create|get|verify" CLI command
group, fake API server routes, and unit + integration tests.
blocked_reason is modelled as a nullable string (not an enum): the API
column is free-text, so an unknown value must not break deserialization.
Verified against the monorepo public-api schema and the live staging API.
There was a problem hiding this comment.
4 issues found across 15 files
Confidence score: 3/5
- In
src/Resend/DomainClaimStatus.cs, deserialization currently has no fallback for unknown enum values, so a newly introduced API status could throwJsonExceptionand fail the entireDomainClaimpayload for clients — add an unknown/default enum mapping or tolerant converter path to preserve forward compatibility. - In
tools/Resend.ApiServer/Controllers/DomainController.cs, retrieve and verify handlers duplicate hard-coded claim construction, which raises drift risk where one fake response changes and the other silently diverges — extract a shared claim response factory/helper used by both routes. - In
tools/Resend.Cli/Domain/Claim/DomainClaimGetCommand.csandtools/Resend.Cli/Domain/Claim/DomainClaimVerifyCommand.cs, leftover "Placeholder" argument descriptions can confuse CLI users and create inconsistent help text — align both to the existing convention (e.g., "Domain identifier").
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tools/Resend.ApiServer/Controllers/DomainController.cs">
<violation number="1" location="tools/Resend.ApiServer/Controllers/DomainController.cs:212">
P3: The retrieve and verify handlers duplicate the entire hard-coded claim construction, so changes to the fake claim response can easily drift between these routes. A shared response factory/helper would keep the two test endpoints consistent.</violation>
</file>
<file name="src/Resend/DomainClaimStatus.cs">
<violation number="1" location="src/Resend/DomainClaimStatus.cs:9">
P2: No fallback for unknown enum values: if the API introduces a new status, deserialization of the entire `DomainClaim` response throws a `JsonException` (SE002). The `blocked_reason` field on `DomainClaim` was consciously modeled as a nullable string to avoid exactly this failure mode, but the required `status` field uses an enum with no unknown-value handling.</violation>
</file>
<file name="tools/Resend.Cli/Domain/Claim/DomainClaimGetCommand.cs">
<violation number="1" location="tools/Resend.Cli/Domain/Claim/DomainClaimGetCommand.cs:16">
P3: Argument description contains leftover scaffolding text "Placeholder" and doesn't match the established convention. Other domain commands (e.g. `DomainRetrieveCommand`) use `"Domain identifier"`.</violation>
</file>
<file name="tools/Resend.Cli/Domain/Claim/DomainClaimVerifyCommand.cs">
<violation number="1" location="tools/Resend.Cli/Domain/Claim/DomainClaimVerifyCommand.cs:14">
P3: The argument description says "Placeholder domain identifier". "Placeholder" reads like a scaffolding placeholder that was left in. Consider updating it to "Domain identifier" (matching the convention in `DomainRetrieveCommand`).</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| /// Status of a domain claim. | ||
| /// </summary> | ||
| /// <see href="https://resend.com/docs/dashboard/domains/claim" /> | ||
| [JsonConverter( typeof( JsonStringEnumValueConverter<DomainClaimStatus> ) )] |
There was a problem hiding this comment.
P2: No fallback for unknown enum values: if the API introduces a new status, deserialization of the entire DomainClaim response throws a JsonException (SE002). The blocked_reason field on DomainClaim was consciously modeled as a nullable string to avoid exactly this failure mode, but the required status field uses an enum with no unknown-value handling.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/Resend/DomainClaimStatus.cs, line 9:
<comment>No fallback for unknown enum values: if the API introduces a new status, deserialization of the entire `DomainClaim` response throws a `JsonException` (SE002). The `blocked_reason` field on `DomainClaim` was consciously modeled as a nullable string to avoid exactly this failure mode, but the required `status` field uses an enum with no unknown-value handling.</comment>
<file context>
@@ -0,0 +1,59 @@
+/// Status of a domain claim.
+/// </summary>
+/// <see href="https://resend.com/docs/dashboard/domains/claim" />
+[JsonConverter( typeof( JsonStringEnumValueConverter<DomainClaimStatus> ) )]
+public enum DomainClaimStatus
+{
</file context>
| /// <summary /> | ||
| [HttpPost] | ||
| [Route( "domains/{id}/claim/verify" )] | ||
| public DomainClaim DomainClaimVerify( [FromRoute] Guid id ) |
There was a problem hiding this comment.
P3: The retrieve and verify handlers duplicate the entire hard-coded claim construction, so changes to the fake claim response can easily drift between these routes. A shared response factory/helper would keep the two test endpoints consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/Resend.ApiServer/Controllers/DomainController.cs, line 212:
<comment>The retrieve and verify handlers duplicate the entire hard-coded claim construction, so changes to the fake claim response can easily drift between these routes. A shared response factory/helper would keep the two test endpoints consistent.</comment>
<file context>
@@ -148,4 +148,88 @@ public ActionResult DomainDelete( [FromRoute] Guid id )
+ /// <summary />
+ [HttpPost]
+ [Route( "domains/{id}/claim/verify" )]
+ public DomainClaim DomainClaimVerify( [FromRoute] Guid id )
+ {
+ _logger.LogDebug( "DomainClaimVerify" );
</file context>
|
|
||
|
|
||
| /// <summary /> | ||
| [Argument( 0, Description = "Placeholder domain identifier" )] |
There was a problem hiding this comment.
P3: Argument description contains leftover scaffolding text "Placeholder" and doesn't match the established convention. Other domain commands (e.g. DomainRetrieveCommand) use "Domain identifier".
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/Resend.Cli/Domain/Claim/DomainClaimGetCommand.cs, line 16:
<comment>Argument description contains leftover scaffolding text "Placeholder" and doesn't match the established convention. Other domain commands (e.g. `DomainRetrieveCommand`) use `"Domain identifier"`.</comment>
<file context>
@@ -0,0 +1,41 @@
+
+
+ /// <summary />
+ [Argument( 0, Description = "Placeholder domain identifier" )]
+ [Required]
+ public Guid? DomainId { get; set; }
</file context>
|
|
||
|
|
||
| /// <summary /> | ||
| [Argument( 0, Description = "Placeholder domain identifier" )] |
There was a problem hiding this comment.
P3: The argument description says "Placeholder domain identifier". "Placeholder" reads like a scaffolding placeholder that was left in. Consider updating it to "Domain identifier" (matching the convention in DomainRetrieveCommand).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/Resend.Cli/Domain/Claim/DomainClaimVerifyCommand.cs, line 14:
<comment>The argument description says "Placeholder domain identifier". "Placeholder" reads like a scaffolding placeholder that was left in. Consider updating it to "Domain identifier" (matching the convention in `DomainRetrieveCommand`).</comment>
<file context>
@@ -0,0 +1,39 @@
+
+
+ /// <summary />
+ [Argument( 0, Description = "Placeholder domain identifier" )]
+ [Required]
+ public Guid? DomainId { get; set; }
</file context>
|
Thanks @cubic-dev-ai. Triage: Addressed (commit 39ac5f5):
Declined, with reasoning:
|
@felipefreitag Triage looks solid. Quick notes on each: P1 (null guard) — even if the API always returns P3 (create JSON drift) — reusing P2 (status enum) — your reasoning is airtight. Making P3 (fake controller duplication) — following the existing test-server style is pragmatic. Refactoring that out of scope is correct. P3 ("Placeholder") — that's the API's own language, so it's precise, not scaffolding. No reason to swap it for something vague. Nothing to push back on here. Everything looks consistent and intentional. Tip: get faster answers by chatting with cubic’s review copilot in the review UI |
Summary
Adds SDK support for the newly-released Domain Claims API, mirroring the Node.js SDK:
POST /domains/claim→DomainClaimAsync(name, region?)andDomainClaimAsync(DomainClaimData)GET /domains/{id}/claim→DomainClaimRetrieveAsync(domainId)POST /domains/{id}/claim/verify→DomainClaimVerifyAsync(domainId)What's included
DomainClaim,DomainClaimRecord,DomainClaimData, and theDomainClaimStatusenum (pending/verified/completed/blocked/expired/superseded/canceled/failed).ResendClient.DomainClaims.cs(partial class, matching the OAuth-grants convention) + three methods onIResend, documented in the existingDomainsregion.domain claim create|get|verifysubcommand group.+00space-separated datetimes, ablocked_reasonvalue, an out-of-order / type-last payload, and an unknownblocked_reason) + 4 integration tests against the in-memory server.Design note:
blocked_reasonModelled as a nullable
string, not an enum. The Node SDK uses a string-union, but the API column is free-text on the server, so aJsonStringEnumValueConverterwould throw on any unrecognized value and break deserialization of the whole response. A regression test covers an unknown value.Verification
domain_id/region, the 8-value statuspgEnum, and the free-textblocked_reasoncolumn all cross-checked againstorigin/mainserver source.curland end-to-end through the SDK (CLI); every field mapped,+00datetimes parsed, test placeholder domain cleaned up.net8.0).Summary by cubic
Adds Domain Claims API support to the SDK and CLI. Create a claim, check its status, and trigger verification for domains already verified by another team; also bumps fallback version to 0.7.0.
New Features
DomainClaimAsync(name, region?),DomainClaimAsync(DomainClaimData),DomainClaimRetrieveAsync(domainId),DomainClaimVerifyAsync(domainId).DomainClaim,DomainClaimRecord,DomainClaimData, andDomainClaimStatus(pending, verified, completed, blocked, expired, superseded, canceled, failed).blocked_reasonis a nullable string to tolerate unknown server values.domain claim create,domain claim get,domain claim verify.Bug Fixes
recordwhen printing claims.domain claim createreuses the shared renderer for consistent output.Written for commit 39ac5f5. Summary will update on new commits.