diff --git a/src/Resend/IResend.cs b/src/Resend/IResend.cs
index ca00b1e..7065b56 100644
--- a/src/Resend/IResend.cs
+++ b/src/Resend/IResend.cs
@@ -353,6 +353,39 @@ public interface IResend
#endregion
+ #region OAuth Grants
+
+ ///
+ /// Lists all OAuth grants.
+ ///
+ ///
+ /// Pagination query.
+ ///
+ ///
+ /// Cancellation token.
+ ///
+ /// Paginated list of OAuth grants.
+ ///
+ Task>> OAuthGrantListAsync( PaginatedQuery? query = null, CancellationToken cancellationToken = default );
+
+ ///
+ /// Revoke an existing OAuth grant. Revoking a grant invalidates every access
+ /// and refresh token issued under it.
+ ///
+ ///
+ /// OAuth grant identifier.
+ ///
+ ///
+ /// Cancellation token.
+ ///
+ ///
+ /// Outcome of the revocation.
+ ///
+ ///
+ Task> OAuthGrantRevokeAsync( Guid oauthGrantId, CancellationToken cancellationToken = default );
+
+ #endregion
+
#region Audiences
///
diff --git a/src/Resend/OAuthGrant.cs b/src/Resend/OAuthGrant.cs
new file mode 100644
index 0000000..c402bbd
--- /dev/null
+++ b/src/Resend/OAuthGrant.cs
@@ -0,0 +1,114 @@
+using System.Text.Json.Serialization;
+
+namespace Resend;
+
+///
+/// OAuth grant, representing access a user has delegated to an OAuth client.
+///
+public class OAuthGrant
+{
+ ///
+ /// OAuth grant identifier.
+ ///
+ [JsonPropertyName( "id" )]
+ public Guid Id { get; set; }
+
+ ///
+ /// Identifier of the OAuth client the grant was issued to.
+ ///
+ [JsonPropertyName( "client_id" )]
+ public Guid ClientId { get; set; }
+
+ ///
+ /// Scopes granted to the OAuth client.
+ ///
+ [JsonPropertyName( "scopes" )]
+ public List Scopes { get; set; } = default!;
+
+ ///
+ /// Resource the grant is restricted to, if any.
+ ///
+ [JsonPropertyName( "resource" )]
+ [JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
+ public string? Resource { get; set; }
+
+ ///
+ /// Moment in which the grant was created.
+ ///
+ [JsonPropertyName( "created_at" )]
+ [JsonConverter( typeof( JsonUtcDateTimeConverter ) )]
+ public DateTime MomentCreated { get; set; }
+
+ ///
+ /// Moment in which the grant was revoked, if it has been revoked.
+ ///
+ [JsonPropertyName( "revoked_at" )]
+ [JsonConverter( typeof( JsonUtcDateTimeConverter ) )]
+ [JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
+ public DateTime? MomentRevoked { get; set; }
+
+ ///
+ /// Reason the grant was revoked, if it has been revoked.
+ ///
+ [JsonPropertyName( "revoked_reason" )]
+ [JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
+ public string? RevokedReason { get; set; }
+
+ ///
+ /// OAuth client the grant was issued to.
+ ///
+ [JsonPropertyName( "client" )]
+ public OAuthGrantClient Client { get; set; } = default!;
+}
+
+
+///
+/// OAuth client associated with a grant.
+///
+public class OAuthGrantClient
+{
+ ///
+ /// Display name of the OAuth client.
+ ///
+ [JsonPropertyName( "name" )]
+ public string Name { get; set; } = default!;
+
+ ///
+ /// URI of the OAuth client logo, if any.
+ ///
+ [JsonPropertyName( "logo_uri" )]
+ [JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
+ public string? LogoUri { get; set; }
+}
+
+
+///
+/// Outcome of revoking an OAuth grant.
+///
+public class OAuthGrantRevoked
+{
+ ///
+ /// Object type discriminator.
+ ///
+ [JsonPropertyName( "object" )]
+ public string Object { get; set; } = default!;
+
+ ///
+ /// OAuth grant identifier.
+ ///
+ [JsonPropertyName( "id" )]
+ public Guid Id { get; set; }
+
+ ///
+ /// Moment in which the grant was revoked.
+ ///
+ [JsonPropertyName( "revoked_at" )]
+ [JsonConverter( typeof( JsonUtcDateTimeConverter ) )]
+ public DateTime MomentRevoked { get; set; }
+
+ ///
+ /// Reason the grant was revoked.
+ ///
+ [JsonPropertyName( "revoked_reason" )]
+ public string RevokedReason { get; set; } = default!;
+}
diff --git a/src/Resend/ResendClient.OAuthGrants.cs b/src/Resend/ResendClient.OAuthGrants.cs
new file mode 100644
index 0000000..7213d61
--- /dev/null
+++ b/src/Resend/ResendClient.OAuthGrants.cs
@@ -0,0 +1,44 @@
+using Microsoft.AspNetCore.WebUtilities;
+using Resend.Payloads;
+
+namespace Resend;
+
+public partial class ResendClient
+{
+ ///
+ public Task>> OAuthGrantListAsync( PaginatedQuery? query = null, CancellationToken cancellationToken = default )
+ {
+ var baseUrl = "/oauth/grants";
+ var url = baseUrl;
+
+ if ( query != null )
+ {
+ var qs = new Dictionary();
+
+ if ( query.Limit.HasValue == true )
+ qs.Add( "limit", query.Limit.Value.ToString() );
+
+ if ( query.Before != null )
+ qs.Add( "before", query.Before );
+
+ if ( query.After != null )
+ qs.Add( "after", query.After );
+
+ url = QueryHelpers.AddQueryString( baseUrl, qs );
+ }
+
+ var req = new HttpRequestMessage( HttpMethod.Get, url );
+
+ return Execute, PaginatedResult>( req, ( x ) => x, cancellationToken );
+ }
+
+
+ ///
+ public Task> OAuthGrantRevokeAsync( Guid oauthGrantId, CancellationToken cancellationToken = default )
+ {
+ var path = $"/oauth/grants/{oauthGrantId}";
+ var req = new HttpRequestMessage( HttpMethod.Delete, path );
+
+ return Execute( req, ( x ) => x, cancellationToken );
+ }
+}
diff --git a/tests/Resend.Tests/ResendClientTests.OAuthGrants.cs b/tests/Resend.Tests/ResendClientTests.OAuthGrants.cs
new file mode 100644
index 0000000..8783a77
--- /dev/null
+++ b/tests/Resend.Tests/ResendClientTests.OAuthGrants.cs
@@ -0,0 +1,38 @@
+namespace Resend.Tests;
+
+///
+public partial class ResendClientTests
+{
+ ///
+ [Fact]
+ public async Task OAuthGrantList()
+ {
+ var resp = await _resend.OAuthGrantListAsync();
+
+ Assert.NotNull( resp );
+ Assert.NotNull( resp.Content );
+ Assert.NotEmpty( resp.Content.Data );
+
+ var grant = resp.Content.Data[ 0 ];
+
+ Assert.NotEqual( Guid.Empty, grant.Id );
+ Assert.Equal( "Resend CLI", grant.Client.Name );
+ Assert.Contains( "emails:send", grant.Scopes );
+ }
+
+
+ ///
+ [Fact]
+ public async Task OAuthGrantRevoke()
+ {
+ var id = Guid.NewGuid();
+
+ var resp = await _resend.OAuthGrantRevokeAsync( id );
+
+ Assert.NotNull( resp );
+ Assert.NotNull( resp.Content );
+ Assert.Equal( "oauth_grant", resp.Content.Object );
+ Assert.Equal( id, resp.Content.Id );
+ Assert.Equal( "revoked_from_api", resp.Content.RevokedReason );
+ }
+}
diff --git a/tools/Resend.ApiServer/Controllers/OAuthGrantController.cs b/tools/Resend.ApiServer/Controllers/OAuthGrantController.cs
new file mode 100644
index 0000000..8d6d825
--- /dev/null
+++ b/tools/Resend.ApiServer/Controllers/OAuthGrantController.cs
@@ -0,0 +1,56 @@
+using Microsoft.AspNetCore.Mvc;
+using Resend.Payloads;
+
+namespace Resend.ApiServer.Controllers;
+
+///
+[ApiController]
+public class OAuthGrantController : ControllerBase
+{
+ private readonly ILogger _logger;
+
+
+ ///
+ public OAuthGrantController( ILogger logger )
+ {
+ _logger = logger;
+ }
+
+
+ ///
+ [HttpGet]
+ [Route( "oauth/grants" )]
+ public PaginatedResult OAuthGrantList()
+ {
+ _logger.LogDebug( "OAuthGrantList" );
+
+ var list = new List();
+ list.Add( new OAuthGrant()
+ {
+ Id = Guid.NewGuid(),
+ ClientId = Guid.NewGuid(),
+ Scopes = new List() { "emails:send" },
+ MomentCreated = DateTime.UtcNow,
+ Client = new OAuthGrantClient() { Name = "Resend CLI" },
+ } );
+
+ return new PaginatedResult() { HasMore = false, Data = list };
+ }
+
+
+ ///
+ [HttpDelete]
+ [Route( "oauth/grants/{id}" )]
+ public OAuthGrantRevoked OAuthGrantRevoke( [FromRoute] Guid id )
+ {
+ _logger.LogDebug( "OAuthGrantRevoke" );
+
+ return new OAuthGrantRevoked()
+ {
+ Object = "oauth_grant",
+ Id = id,
+ MomentRevoked = DateTime.UtcNow,
+ RevokedReason = "revoked_from_api",
+ };
+ }
+}
diff --git a/tools/Resend.Cli/OAuthGrant/OAuthGrantListCommand.cs b/tools/Resend.Cli/OAuthGrant/OAuthGrantListCommand.cs
new file mode 100644
index 0000000..7305b67
--- /dev/null
+++ b/tools/Resend.Cli/OAuthGrant/OAuthGrantListCommand.cs
@@ -0,0 +1,64 @@
+using McMaster.Extensions.CommandLineUtils;
+using Spectre.Console;
+using System.Text.Json;
+
+namespace Resend.Cli.OAuthGrant;
+
+///
+[Command( "list", Description = "Lists OAuth grants" )]
+public class OAuthGrantListCommand
+{
+ private readonly IResend _resend;
+
+
+ ///
+ [Option( "-j|--json", CommandOptionType.NoValue, Description = "Emit output as JSON array" )]
+ public bool InJson { get; set; }
+
+
+ ///
+ public OAuthGrantListCommand( IResend resend )
+ {
+ _resend = resend;
+ }
+
+
+ ///
+ public async Task OnExecuteAsync()
+ {
+ var res = await _resend.OAuthGrantListAsync();
+ var grants = res.Content.Data;
+
+
+ if ( this.InJson == true )
+ {
+ var jso = new JsonSerializerOptions() { WriteIndented = true };
+ var json = JsonSerializer.Serialize( grants, jso );
+
+ Console.WriteLine( json );
+ }
+ else
+ {
+ var table = new Table();
+ table.Border = TableBorder.SimpleHeavy;
+ table.AddColumn( "Grant Id" );
+ table.AddColumn( "Client" );
+ table.AddColumn( "Scopes" );
+ table.AddColumn( "Created" );
+
+ foreach ( var g in grants )
+ {
+ table.AddRow(
+ new Markup( g.Id.ToString() ),
+ new Markup( Markup.Escape( g.Client.Name ) ),
+ new Markup( Markup.Escape( string.Join( ", ", g.Scopes ) ) ),
+ new Markup( g.MomentCreated.ToString() )
+ );
+ }
+
+ AnsiConsole.Write( table );
+ }
+
+ return 0;
+ }
+}
diff --git a/tools/Resend.Cli/OAuthGrant/OAuthGrantRevokeCommand.cs b/tools/Resend.Cli/OAuthGrant/OAuthGrantRevokeCommand.cs
new file mode 100644
index 0000000..d1080ba
--- /dev/null
+++ b/tools/Resend.Cli/OAuthGrant/OAuthGrantRevokeCommand.cs
@@ -0,0 +1,33 @@
+using McMaster.Extensions.CommandLineUtils;
+using System.ComponentModel.DataAnnotations;
+
+namespace Resend.Cli.OAuthGrant;
+
+///
+[Command( "revoke", Description = "Revoke an OAuth grant" )]
+public class OAuthGrantRevokeCommand
+{
+ private readonly IResend _resend;
+
+
+ ///
+ [Argument( 0, Description = "OAuth grant identifier" )]
+ [Required]
+ public Guid? GrantId { get; set; }
+
+
+ ///
+ public OAuthGrantRevokeCommand( IResend resend )
+ {
+ _resend = resend;
+ }
+
+
+ ///
+ public async Task OnExecuteAsync()
+ {
+ await _resend.OAuthGrantRevokeAsync( this.GrantId!.Value );
+
+ return 0;
+ }
+}
diff --git a/tools/Resend.Cli/OAuthGrantCommand.cs b/tools/Resend.Cli/OAuthGrantCommand.cs
new file mode 100644
index 0000000..02c0b86
--- /dev/null
+++ b/tools/Resend.Cli/OAuthGrantCommand.cs
@@ -0,0 +1,17 @@
+using McMaster.Extensions.CommandLineUtils;
+
+namespace Resend.Cli;
+
+///
+[Command( "oauth-grant", Description = "OAuth grant management" )]
+[Subcommand( typeof( OAuthGrant.OAuthGrantListCommand ) )]
+[Subcommand( typeof( OAuthGrant.OAuthGrantRevokeCommand ) )]
+public class OAuthGrantCommand
+{
+ ///
+ public int OnExecute( CommandLineApplication app )
+ {
+ app.ShowHelp();
+ return 1;
+ }
+}
diff --git a/tools/Resend.Cli/Program.cs b/tools/Resend.Cli/Program.cs
index 21cea20..7e7bfe9 100644
--- a/tools/Resend.Cli/Program.cs
+++ b/tools/Resend.Cli/Program.cs
@@ -12,6 +12,7 @@ namespace Resend.Cli;
[Subcommand( typeof( ContactPropCommand ) )]
[Subcommand( typeof( DomainCommand ) )]
[Subcommand( typeof( EmailCommand ) )]
+[Subcommand( typeof( OAuthGrantCommand ) )]
[Subcommand( typeof( ReceiveCommand ) )]
[Subcommand( typeof( SegmentCommand ) )]
[Subcommand( typeof( TemplateCommand ) )]