Add Contacts and Contact Properties API#90
Conversation
There was a problem hiding this comment.
20 issues found across 26 files
Prompt for AI agents (all 20 issues)
Understand the root cause of the following 20 issues and fix them.
<file name="tools/Resend.Cli/Contact/Segment/ContactSegmentDeleteCommand.cs">
<violation number="1" location="tools/Resend.Cli/Contact/Segment/ContactSegmentDeleteCommand.cs:34">
Rule violated: **API Key Permission Check SDK Methods**
This new CLI command introduces the Resend SDK call `ContactRemoveFromSegmentAsync`, which is a new API operation that will fail in production if the existing API key lacks the necessary segment-removal permission. Please confirm and document that the production API key is authorized for this call to comply with our API key permission policy.</violation>
</file>
<file name="src/Resend/ResendClient.Contacts.cs">
<violation number="1" location="src/Resend/ResendClient.Contacts.cs:11">
Rule violated: **API Key Permission Check SDK Methods**
Per the API Key Permission Check SDK Methods guideline, adding this POST /audiences/{audienceId}/contacts call introduces new Resend API operations. Please confirm that the production API key used by this SDK has the necessary permissions/scopes for contact creation and the other new contact/segment/topic endpoints in this change so we avoid permission failures after deployment.</violation>
<violation number="2" location="src/Resend/ResendClient.Contacts.cs:37">
Interpolating email directly into the path allows reserved URI characters (e.g., %, ?) from valid addresses to break the request. Encode the email before building the URL.</violation>
</file>
<file name="src/Resend/IResend.cs">
<violation number="1" location="src/Resend/IResend.cs:558">
Rule violated: **API Key Permission Check SDK Methods**
These new Resend contact segment/topic/property SDK calls expand required provider capabilities. Please double-check with the Resend docs that your production API keys are granted the scopes needed for segment management, topic subscription updates, and custom property CRUD to avoid permission failures.</violation>
</file>
<file name="tools/Resend.Cli/Contact/Segment/ContactSegmentListCommand.cs">
<violation number="1" location="tools/Resend.Cli/Contact/Segment/ContactSegmentListCommand.cs:34">
Rule violated: **API Key Permission Check SDK Methods**
Calling the new Resend ContactListSegmentsAsync endpoint adds a third-party SDK operation whose required API key permissions need explicit confirmation. Please verify that the production keys include access for listing contact segments to avoid runtime failures.</violation>
</file>
<file name="src/Resend/ResendClient.ContactProps.cs">
<violation number="1" location="src/Resend/ResendClient.ContactProps.cs:10">
Rule violated: **API Key Permission Check SDK Methods**
These new SDK wrappers add list/create/retrieve/update/delete calls against Resend's `/contact-properties` endpoints. Per the API Key Permission Check SDK Methods guideline, please confirm the production Resend API keys already include permissions for managing contact properties so we do not ship an endpoint the keys cannot call.</violation>
</file>
<file name="tools/Resend.Cli/Contact/Segment/ContactSegmentAddCommand.cs">
<violation number="1" location="tools/Resend.Cli/Contact/Segment/ContactSegmentAddCommand.cs:34">
Rule violated: **API Key Permission Check SDK Methods**
The new ContactAddToSegmentAsync SDK call introduces a segment-modification operation; please ensure the production API keys are confirmed to have the required permissions so this command does not fail at runtime.</violation>
</file>
<file name="tools/Resend.Cli/ContactProp/ContactPropDeleteCommand.cs">
<violation number="1" location="tools/Resend.Cli/ContactProp/ContactPropDeleteCommand.cs:29">
Rule violated: **API Key Permission Check SDK Methods**
This command adds a new call to `ContactPropDeleteAsync` on the Resend SDK. Please confirm the production API key has the required permission to delete contact properties so this operation does not fail after deployment.</violation>
</file>
<file name="tools/Resend.Cli/ContactProp/ContactPropAddCommand.cs">
<violation number="1" location="tools/Resend.Cli/ContactProp/ContactPropAddCommand.cs:55">
Rule violated: **API Key Permission Check SDK Methods**
This command now invokes the Resend SDK’s `ContactPropCreateAsync` to create contact properties. Please confirm that the production API keys have the contact-property create permission so this new operation does not fail after deployment.</violation>
</file>
<file name="tools/Resend.Cli/Contact/Topic/ContactTopicListCommand.cs">
<violation number="1" location="tools/Resend.Cli/Contact/Topic/ContactTopicListCommand.cs:34">
Rule violated: **API Key Permission Check SDK Methods**
The new ContactListTopicsAsync call adds a Resend SDK operation; please confirm the production API keys have the necessary permissions to list contact topics so this command doesn’t fail after deployment.</violation>
</file>
<file name="tools/Resend.Cli/ContactProp/ContactPropRetrieveCommand.cs">
<violation number="1" location="tools/Resend.Cli/ContactProp/ContactPropRetrieveCommand.cs:35">
Rule violated: **API Key Permission Check SDK Methods**
This command introduces a new Resend SDK operation (`ContactPropRetrieveAsync`). Please ensure the production API keys are updated/verified to include contact property retrieval permissions before release to avoid runtime authorization failures.</violation>
</file>
<file name="tools/Resend.Cli/ContactProp/ContactPropListCommand.cs">
<violation number="1" location="tools/Resend.Cli/ContactProp/ContactPropListCommand.cs:28">
Rule violated: **API Key Permission Check SDK Methods**
`ContactPropListAsync` is a new Resend SDK call that will run under existing production API keys. Please confirm those keys include permission to list contact properties so the CLI doesn’t start failing with permission errors after deployment.</violation>
<violation number="2" location="tools/Resend.Cli/ContactProp/ContactPropListCommand.cs:54">
Escape the default value before passing it to Spectre.Console.Markup so user-provided fallback strings containing markup characters do not break rendering.</violation>
</file>
<file name="tools/Resend.Cli/ContactProp/ContactPropUpdateCommand.cs">
<violation number="1" location="tools/Resend.Cli/ContactProp/ContactPropUpdateCommand.cs:42">
`DefaultValue` is initialized to null, so running the command without `--default` clears the property's existing fallback value instead of leaving it unchanged.</violation>
<violation number="2" location="tools/Resend.Cli/ContactProp/ContactPropUpdateCommand.cs:57">
Rule violated: **API Key Permission Check SDK Methods**
Introducing this ContactPropUpdateAsync call adds a new Resend Contact Properties API operation; please confirm the production API keys are provisioned for contact property updates so the CLI doesn’t hit permission failures after deployment.</violation>
</file>
<file name="tools/Resend.Cli/Contact/Topic/ContactTopicSetCommand.cs">
<violation number="1" location="tools/Resend.Cli/Contact/Topic/ContactTopicSetCommand.cs:58">
Rule violated: **API Key Permission Check SDK Methods**
ContactTopicSetCommand now calls the Resend SDK’s ContactUpdateTopicsAsync to mutate contact topic subscriptions. Per the “API Key Permission Check SDK Methods” rule, we need confirmation that the production API keys are authorized to update topic subscriptions; otherwise this new command can fail once deployed. Please verify the keys include this capability.</violation>
</file>
<file name="tools/Resend.Cli/Program.cs">
<violation number="1" location="tools/Resend.Cli/Program.cs:13">
Rule violated: **API Key Permission Check SDK Methods**
Registering ContactPropCommand adds CLI flows that call the new Resend contact property APIs (e.g., ContactPropCreateAsync, ContactPropRetrieveAsync, ContactPropUpdateAsync). Please confirm the production API keys have the necessary permissions for these operations to avoid post-deploy failures.</violation>
<violation number="2" location="tools/Resend.Cli/Program.cs:13">
The mock API server (`Resend.ApiServer`) is missing implementations for the new Contact Properties, Segments, and Topics APIs. The client and CLI tools have been updated to call these endpoints, but they will fail because the server does not handle the corresponding routes (e.g., `/contact-properties`). This creates a significant contract violation between the client and its test server.</violation>
</file>
<file name="tools/Resend.ApiServer/Controllers/ContactController.cs">
<violation number="1" location="tools/Resend.ApiServer/Controllers/ContactController.cs:128">
There is a client-server contract mismatch in the pagination implementation for listing contacts. The client sends pagination parameters (`limit`, `before`, `after`), but the mock API server's `ContactList` endpoint does not accept them, rendering the pagination feature non-functional and untestable against the mock server.</violation>
</file>
<file name="src/Resend/ContactPropertyData.cs">
<violation number="1" location="src/Resend/ContactPropertyData.cs:9">
Update the XML summary to describe the property type to keep the documentation accurate.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| /// <summary /> | ||
| public async Task<int> OnExecuteAsync() | ||
| { | ||
| await _resend.ContactRemoveFromSegmentAsync( this.ContactId!.Value, this.SegmentId!.Value ); |
There was a problem hiding this comment.
Rule violated: API Key Permission Check SDK Methods
This new CLI command introduces the Resend SDK call ContactRemoveFromSegmentAsync, which is a new API operation that will fail in production if the existing API key lacks the necessary segment-removal permission. Please confirm and document that the production API key is authorized for this call to comply with our API key permission policy.
Prompt for AI agents
Address the following comment on tools/Resend.Cli/Contact/Segment/ContactSegmentDeleteCommand.cs at line 34:
<comment>This new CLI command introduces the Resend SDK call `ContactRemoveFromSegmentAsync`, which is a new API operation that will fail in production if the existing API key lacks the necessary segment-removal permission. Please confirm and document that the production API key is authorized for this call to comply with our API key permission policy.</comment>
<file context>
@@ -0,0 +1,38 @@
+ /// <summary />
+ public async Task<int> OnExecuteAsync()
+ {
+ await _resend.ContactRemoveFromSegmentAsync( this.ContactId!.Value, this.SegmentId!.Value );
+
+ return 0;
</file context>
| public partial class ResendClient | ||
| { | ||
| /// <inheritdoc/> | ||
| public Task<ResendResponse<Guid>> ContactAddAsync( Guid audienceId, ContactData data, CancellationToken cancellationToken = default ) |
There was a problem hiding this comment.
Rule violated: API Key Permission Check SDK Methods
Per the API Key Permission Check SDK Methods guideline, adding this POST /audiences/{audienceId}/contacts call introduces new Resend API operations. Please confirm that the production API key used by this SDK has the necessary permissions/scopes for contact creation and the other new contact/segment/topic endpoints in this change so we avoid permission failures after deployment.
Prompt for AI agents
Address the following comment on src/Resend/ResendClient.Contacts.cs at line 11:
<comment>Per the API Key Permission Check SDK Methods guideline, adding this POST /audiences/{audienceId}/contacts call introduces new Resend API operations. Please confirm that the production API key used by this SDK has the necessary permissions/scopes for contact creation and the other new contact/segment/topic endpoints in this change so we avoid permission failures after deployment.</comment>
<file context>
@@ -0,0 +1,199 @@
+public partial class ResendClient
+{
+ /// <inheritdoc/>
+ public Task<ResendResponse<Guid>> ContactAddAsync( Guid audienceId, ContactData data, CancellationToken cancellationToken = default )
+ {
+ if ( data.Email == null )
</file context>
| /// <param name="query">Paginated query.</param> | ||
| /// <param name="cancellationToken">Cancellation token.</param> | ||
| /// <returns>List of segments.</returns> | ||
| Task<ResendResponse<PaginatedResult<Segment>>> ContactListSegmentsAsync( Guid contactId, PaginatedQuery? query = null, CancellationToken cancellationToken = default ); |
There was a problem hiding this comment.
Rule violated: API Key Permission Check SDK Methods
These new Resend contact segment/topic/property SDK calls expand required provider capabilities. Please double-check with the Resend docs that your production API keys are granted the scopes needed for segment management, topic subscription updates, and custom property CRUD to avoid permission failures.
Prompt for AI agents
Address the following comment on src/Resend/IResend.cs at line 558:
<comment>These new Resend contact segment/topic/property SDK calls expand required provider capabilities. Please double-check with the Resend docs that your production API keys are granted the scopes needed for segment management, topic subscription updates, and custom property CRUD to avoid permission failures.</comment>
<file context>
@@ -531,19 +531,112 @@ public interface IResend
+ /// <param name="query">Paginated query.</param>
+ /// <param name="cancellationToken">Cancellation token.</param>
+ /// <returns>List of segments.</returns>
+ Task<ResendResponse<PaginatedResult<Segment>>> ContactListSegmentsAsync( Guid contactId, PaginatedQuery? query = null, CancellationToken cancellationToken = default );
+
+ /// <summary>
</file context>
| /// <summary /> | ||
| public async Task<int> OnExecuteAsync() | ||
| { | ||
| var res = await _resend.ContactListSegmentsAsync( this.ContactId!.Value ); |
There was a problem hiding this comment.
Rule violated: API Key Permission Check SDK Methods
Calling the new Resend ContactListSegmentsAsync endpoint adds a third-party SDK operation whose required API key permissions need explicit confirmation. Please verify that the production keys include access for listing contact segments to avoid runtime failures.
Prompt for AI agents
Address the following comment on tools/Resend.Cli/Contact/Segment/ContactSegmentListCommand.cs at line 34:
<comment>Calling the new Resend ContactListSegmentsAsync endpoint adds a third-party SDK operation whose required API key permissions need explicit confirmation. Please verify that the production keys include access for listing contact segments to avoid runtime failures.</comment>
<file context>
@@ -0,0 +1,66 @@
+ /// <summary />
+ public async Task<int> OnExecuteAsync()
+ {
+ var res = await _resend.ContactListSegmentsAsync( this.ContactId!.Value );
+ var rows = res.Content.Data;
+
</file context>
| public partial class ResendClient | ||
| { | ||
| /// <inheritdoc /> | ||
| public Task<ResendResponse<PaginatedResult<ContactProperty>>> ContactPropListAsync( PaginatedQuery? query = null, CancellationToken cancellationToken = default ) |
There was a problem hiding this comment.
Rule violated: API Key Permission Check SDK Methods
These new SDK wrappers add list/create/retrieve/update/delete calls against Resend's /contact-properties endpoints. Per the API Key Permission Check SDK Methods guideline, please confirm the production Resend API keys already include permissions for managing contact properties so we do not ship an endpoint the keys cannot call.
Prompt for AI agents
Address the following comment on src/Resend/ResendClient.ContactProps.cs at line 10:
<comment>These new SDK wrappers add list/create/retrieve/update/delete calls against Resend's `/contact-properties` endpoints. Per the API Key Permission Check SDK Methods guideline, please confirm the production Resend API keys already include permissions for managing contact properties so we do not ship an endpoint the keys cannot call.</comment>
<file context>
@@ -0,0 +1,74 @@
+public partial class ResendClient
+{
+ /// <inheritdoc />
+ public Task<ResendResponse<PaginatedResult<ContactProperty>>> ContactPropListAsync( PaginatedQuery? query = null, CancellationToken cancellationToken = default )
+ {
+ var baseUrl = "/contact-properties";
</file context>
| [HttpGet] | ||
| [Route( "audiences/{audienceId}/contacts" )] | ||
| public ListOf<Contact> ContactList( [FromRoute] Guid audienceId ) | ||
| public PaginatedResult<Contact> ContactList( [FromRoute] Guid audienceId ) |
There was a problem hiding this comment.
There is a client-server contract mismatch in the pagination implementation for listing contacts. The client sends pagination parameters (limit, before, after), but the mock API server's ContactList endpoint does not accept them, rendering the pagination feature non-functional and untestable against the mock server.
Prompt for AI agents
Address the following comment on tools/Resend.ApiServer/Controllers/ContactController.cs at line 128:
<comment>There is a client-server contract mismatch in the pagination implementation for listing contacts. The client sends pagination parameters (`limit`, `before`, `after`), but the mock API server's `ContactList` endpoint does not accept them, rendering the pagination feature non-functional and untestable against the mock server.</comment>
<file context>
@@ -125,7 +125,7 @@ public ActionResult ContactDeleteByEmail( [FromRoute] Guid audienceId, string em
[HttpGet]
[Route( "audiences/{audienceId}/contacts" )]
- public ListOf<Contact> ContactList( [FromRoute] Guid audienceId )
+ public PaginatedResult<Contact> ContactList( [FromRoute] Guid audienceId )
{
_logger.LogDebug( "ContactList" );
</file context>
| public class ContactPropertyData | ||
| { | ||
| /// <summary> | ||
| /// Property key. |
There was a problem hiding this comment.
Update the XML summary to describe the property type to keep the documentation accurate.
Prompt for AI agents
Address the following comment on src/Resend/ContactPropertyData.cs at line 9:
<comment>Update the XML summary to describe the property type to keep the documentation accurate.</comment>
<file context>
@@ -0,0 +1,31 @@
+public class ContactPropertyData
+{
+ /// <summary>
+ /// Property key.
+ /// </summary>
+ /// <remarks>
</file context>
| /// Property key. | |
| /// Property type. |
| new Markup( c.Id.ToString() ), | ||
| new Markup( c.Key ), | ||
| new Markup( c.PropertyType.ToString() ), | ||
| new Markup( c.DefaultValue?.ToString() ?? "" ), |
There was a problem hiding this comment.
Escape the default value before passing it to Spectre.Console.Markup so user-provided fallback strings containing markup characters do not break rendering.
Prompt for AI agents
Address the following comment on tools/Resend.Cli/ContactProp/ContactPropListCommand.cs at line 54:
<comment>Escape the default value before passing it to Spectre.Console.Markup so user-provided fallback strings containing markup characters do not break rendering.</comment>
<file context>
@@ -0,0 +1,64 @@
+ new Markup( c.Id.ToString() ),
+ new Markup( c.Key ),
+ new Markup( c.PropertyType.ToString() ),
+ new Markup( c.DefaultValue?.ToString() ?? "" ),
+ new Markup( c.MomentCreated.ToString() )
+ );
</file context>
| new Markup( c.DefaultValue?.ToString() ?? "" ), | |
| new Markup( Markup.Escape( c.DefaultValue?.ToString() ?? string.Empty ) ), |
| /* | ||
| * | ||
| */ | ||
| object? def = null; |
There was a problem hiding this comment.
DefaultValue is initialized to null, so running the command without --default clears the property's existing fallback value instead of leaving it unchanged.
Prompt for AI agents
Address the following comment on tools/Resend.Cli/ContactProp/ContactPropUpdateCommand.cs at line 42:
<comment>`DefaultValue` is initialized to null, so running the command without `--default` clears the property's existing fallback value instead of leaving it unchanged.</comment>
<file context>
@@ -0,0 +1,64 @@
+ /*
+ *
+ */
+ object? def = null;
+
+ if ( this.Default != null )
</file context>
| object? def = null; | |
| object? def = prop.DefaultValue; |
| /// <inheritdoc/> | ||
| public Task<ResendResponse<Contact>> ContactRetrieveByEmailAsync( Guid audienceId, string email, CancellationToken cancellationToken = default ) | ||
| { | ||
| var path = $"/audiences/{audienceId}/contacts/{email}"; |
There was a problem hiding this comment.
Interpolating email directly into the path allows reserved URI characters (e.g., %, ?) from valid addresses to break the request. Encode the email before building the URL.
Prompt for AI agents
Address the following comment on src/Resend/ResendClient.Contacts.cs at line 37:
<comment>Interpolating email directly into the path allows reserved URI characters (e.g., %, ?) from valid addresses to break the request. Encode the email before building the URL.</comment>
<file context>
@@ -0,0 +1,199 @@
+ /// <inheritdoc/>
+ public Task<ResendResponse<Contact>> ContactRetrieveByEmailAsync( Guid audienceId, string email, CancellationToken cancellationToken = default )
+ {
+ var path = $"/audiences/{audienceId}/contacts/{email}";
+ var req = new HttpRequestMessage( HttpMethod.Get, path );
+
</file context>
| var path = $"/audiences/{audienceId}/contacts/{email}"; | |
| var path = $"/audiences/{audienceId}/contacts/{Uri.EscapeDataString( email )}"; |
Summary by cubic
Added Contacts and Contact Properties APIs with pagination, segment membership, topic subscriptions, and property CRUD. The CLI now supports managing segments, topics, and properties, and contact listing is paginated.
New Features
Migration
Written for commit 567cc7d. Summary will update automatically on new commits.