Repository: svix/svix-webhooks (language: rust)

Issue:
Rust ReplayIn is private but should be public
## Bug Report

Version: 0.76.0

The new replay missing struct is private and cannot be imported. It seems to be because it's not exported at pub in the api.rs file.

```
use crate::models::{
    ..., ReplayIn,
};
pub use crate::models::{
    ApplicationIn,..., SHOULD BE HERE
};
```


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/csharp-lint.yml b/.github/workflows/csharp-lint.yml
index 5b63f9107..95bd37b60 100644
--- a/.github/workflows/csharp-lint.yml
+++ b/.github/workflows/csharp-lint.yml
@@ -4,6 +4,7 @@ on:
   pull_request:
     paths:
       - "csharp/**"
+      - "openapi.json"
 
 jobs:
   dotnet:
diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml
index 0e34aa75b..d0e5672f9 100644
--- a/.github/workflows/go-lint.yml
+++ b/.github/workflows/go-lint.yml
@@ -3,6 +3,7 @@ on:
   pull_request:
     paths:
       - "go/**"
+      - "openapi.json"
 jobs:
   build:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/java-lint.yml b/.github/workflows/java-lint.yml
index da68cf397..6a316a6a8 100644
--- a/.github/workflows/java-lint.yml
+++ b/.github/workflows/java-lint.yml
@@ -3,6 +3,7 @@ on:
   pull_request:
     paths:
       - "java/**"
+      - "openapi.json"
 jobs:
   dotnet:
     name: Java Lint
diff --git a/.github/workflows/javascript-lint.yml b/.github/workflows/javascript-lint.yml
index 9bc85c418..41d986bbb 100644
--- a/.github/workflows/javascript-lint.yml
+++ b/.github/workflows/javascript-lint.yml
@@ -3,6 +3,7 @@ on:
   pull_request:
     paths:
       - "javascript/**"
+      - "openapi.json"
 jobs:
   build:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/kotlin-lint.yml b/.github/workflows/kotlin-lint.yml
index a91b36513..ee4874d01 100644
--- a/.github/workflows/kotlin-lint.yml
+++ b/.github/workflows/kotlin-lint.yml
@@ -3,6 +3,7 @@ on:
   pull_request:
     paths:
       - "kotlin/**"
+      - "openapi.json"
 jobs:
   kotlin:
     name: Kotlin Lint
diff --git a/.github/workflows/other-lint.yml b/.github/workflows/other-lint.yml
index a27a312e3..3cce45215 100644
--- a/.github/workflows/other-lint.yml
+++ b/.github/workflows/other-lint.yml
@@ -6,6 +6,7 @@ on:
       - "go/**"
       - "javascript/**"
       - "python/**"
+      - "openapi.json"
 
 jobs:
   build:
diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml
index 88b6c3759..80f1e0112 100644
--- a/.github/workflows/python-lint.yml
+++ b/.github/workflows/python-lint.yml
@@ -3,6 +3,7 @@ on:
   pull_request:
     paths:
       - "python/**"
+      - "openapi.json"
 jobs:
   build:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/ruby-lint.yml b/.github/workflows/ruby-lint.yml
index 062839087..c022721ac 100644
--- a/.github/workflows/ruby-lint.yml
+++ b/.github/workflows/ruby-lint.yml
@@ -4,6 +4,7 @@ on:
   pull_request:
     paths:
       - "ruby/**"
+      - "openapi.json"
 
 jobs:
   dotnet:
diff --git a/.github/workflows/rust-lint.yml b/.github/workflows/rust-lint.yml
index 386a593af..8c9c4c5c8 100644
--- a/.github/workflows/rust-lint.yml
+++ b/.github/workflows/rust-lint.yml
@@ -11,6 +11,7 @@ on:
     paths:
       - 'rust/**'
       - '.github/workflows/rust-lint.yml'
+      - "openapi.json"
 
 jobs:
   test-versions:
diff --git a/.version b/.version
index c52842c46..c85a67dc1 100644
--- a/.version
+++ b/.version
@@ -1,1 +1,1 @@
-0.75.0
+0.81.0
\ No newline at end of file
diff --git a/ChangeLog.md b/ChangeLog.md
index b384c7f48..f92819c91 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -3,6 +3,52 @@
 ## Next
 * 
 
+## Version 0.81.0
+* Libs: add support for creating application when creating a message
+* Libs/Go: bump Go version
+
+## Version 0.80.0
+* Libs: add `prev_iterator` and `order` support to application list
+* Libs: add `prev_iterator` support to event type list
+* Libs/C#: **[Breaking]** change default value for `SvixOptions.Throw` to `true`
+
+## Version 0.79.0
+* Server: support prev_iterator for application and event type listing
+* Server: fix returning of 409 (CONFLICT) when inserting/patching an application with a conflicting `uid`
+* Libs/Ruby: require (reexport) app portal models in ruby (fixing errors)
+* Libs/C#: fix MessageAttempt querying when not filtering by status and code
+
+## Version 0.78.0
+* Server: add `order` query parameter for sorting endpoints
+* Server: fix default sort order of endpoints to `desc` to match prod
+* Libs: add support for `prev_iterator` for application and endpoints
+* Libs/JS: fix sign function to support non-round dates
+* Libs/Go: **[Breaking]** accept a context parameter in all Go lib methods
+
+## Version 0.77.0
+* Server: fix event_type array query parsing
+* Server: fix bad `?channel=` queries
+* Server: fire operational webhook on endpoint secret rotation
+* Server: implement bidirectional pagination for endpoints
+* Libs/Rust: glob-reexport all generated models in Rust
+
+## Version 0.76.1
+* Server: fix `/attempt/endpoint`'s broken `?channel=` query
+* Libs/Rust: add missing exports to a few API endpoints
+* Libs: fix naming of replay-missing methods in libraries (all but Rust)
+
+## Version 0.76.0
+* Server: add org_id and app_id to main tracing span
+* Server: make `wait_for` timeout early and retry
+* Server: add since/until query params to endpoint stats
+* Server: add endpoints to expunge payload and response
+* Server: clarify error message and documentation around filtered IP addresses
+* Server: fix error message with endpoint filter types validation
+* Server: fix `?after=<time>` ID Parsing in paginated endpoints
+* Libs: add endpoints to expunge payload and response
+* Libs: add replay missing messages functions
+* Libs: add transformations APIs
+
 ## Version 0.75.0
 * Server: add built in SSRF support (no need for an external proxy)
 * Server: many worker improvements (see #704 for details)
diff --git a/README.md b/README.md
index ae8f6a2ec..3db3df7b4 100644
--- a/README.md
+++ b/README.md
@@ -238,6 +238,8 @@ There are two configuration variables `db_pool_max_size` and `redis_pool_max_siz
 
 They default to a max size of 20, but higher values can significantly increase performance if your database can handle it.
 
+### SSRF Attacks and Internal IP Addresses
+To prevent SSRF attacks, message dispatches to internal IP addresses are blocked by default. However we understand that this doesn't meet the needs of every user say, for example, the service can only be accessed internally. To bypass these restrictions, see the `whitelist_subnets` configuration option, which accepts an array of CIDR-notation subnets to allow messages to be dispatched to.
 
 ### Webhook signature scheme (symmetric vs asymmetric)
 
diff --git a/csharp/Svix/Application.cs b/csharp/Svix/Application.cs
index 863dd1950..6b9255f24 100644
--- a/csharp/Svix/Application.cs
+++ b/csharp/Svix/Application.cs
@@ -163,6 +163,7 @@ public List<ApplicationOut> List(ListOptions options = null, string idempotencyK
                 var lResponse = _applicationApi.ListApplicationsApiV1AppGet(
                     options?.Iterator,
                     options?.Limit,
+                    options?.Order,
                     idempotencyKey);
 
                 return lResponse?.Data;
@@ -185,6 +186,7 @@ public async Task<List<ApplicationOut>> ListAsync(ListOptions options = null, st
                 var lResponse = await _applicationApi.ListApplicationsApiV1AppGetAsync(
                     options?.Iterator,
                     options?.Limit,
+                    options?.Order,
                     idempotencyKey,
                     cancellationToken);
 
diff --git a/csharp/Svix/Endpoint.cs b/csharp/Svix/Endpoint.cs
index beb9cd62a..f63266414 100644
--- a/csharp/Svix/Endpoint.cs
+++ b/csharp/Svix/Endpoint.cs
@@ -260,6 +260,7 @@ public List<EndpointOut> List(string appId, ListOptions options = null, string i
                     appId,
                     options?.Iterator,
                     options?.Limit,
+                    options?.Order,
                     idempotencyKey);
 
                 return lEndpoints?.Data;
@@ -284,6 +285,7 @@ public async Task<List<EndpointOut>> ListAsync(string appId, ListOptions options
                     appId,
                     options?.Iterator,
                     options?.Limit,
+                    options?.Order,
                     idempotencyKey,
                     cancellationToken);
 
@@ -358,7 +360,7 @@ public bool Recover(string appId, string endpointId, RecoverIn recover, string i
                     recover,
                     idempotencyKey);
 
-                return lResponse.StatusCode == HttpStatusCode.NoContent;
+                return lResponse.StatusCode == HttpStatusCode.Accepted;
             }
             catch (ApiException e)
             {
@@ -383,7 +385,7 @@ public async Task<bool> RecoverAsync(string appId, string endpointId, RecoverIn
                     idempotencyKey,
                     cancellationToken);
 
-                return lResponse.StatusCode == HttpStatusCode.NoContent;
+                return lResponse.StatusCode == HttpStatusCode.Accepted;
             }
             catch (ApiException e)
             {
@@ -584,5 +586,146 @@ public async Task<EndpointStats> GetStatsAsync(string appId, string endpointId,
                 return null;
             }
         }
+
+        public bool ReplayMissing(string appId, string endpointId, ReplayIn replayIn,
+            string idempotencyKey = default)
+        {
+            try
+            {
+                var response = _endpointApi.ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostWithHttpInfo(
+                    appId,
+                    endpointId,
+                    replayIn,
+                    idempotencyKey);
+
+                return response.StatusCode == HttpStatusCode.Accepted;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(ReplayMissing)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
+
+        public async Task<bool> ReplayMissingAsync(string appId, string endpointId, ReplayIn replayIn,
+            string idempotencyKey = default, CancellationToken cancellationToken = default)
+        {
+            try
+            {
+                var response = await _endpointApi.ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostWithHttpInfoAsync(
+                    appId,
+                    endpointId,
+                    replayIn,
+                    idempotencyKey,
+                    cancellationToken);
+
+                return response.StatusCode == HttpStatusCode.Accepted;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(ReplayMissingAsync)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
+
+        public EndpointTransformationOut TransformationGet(string appId, string endpointId, string idempotencyKey = default)
+        {
+            try
+            {
+                var lTransformation = _endpointApi.GetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationGet(
+                    appId,
+                    endpointId,
+                    idempotencyKey);
+
+                return lTransformation;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(TransformationGet)} failed");
+
+                if (Throw)
+                    throw;
+
+                return null;
+            }
+        }
+
+        public async Task<EndpointTransformationOut> TransformationGetAsync(string appId, string endpointId, string idempotencyKey = default, CancellationToken cancellationToken = default)
+        {
+            try
+            {
+                var lTransformation = await _endpointApi.GetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationGetAsync(
+                    appId,
+                    endpointId,
+                    idempotencyKey,
+                    cancellationToken);
+
+                return lTransformation;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(TransformationGetAsync)} failed");
+
+                if (Throw)
+                    throw;
+
+                return null;
+            }
+        }
+
+        public bool TransformationPartialUpdate(string appId, string endpointId, EndpointTransformationIn endpointTransformationIn, string idempotencyKey = default)
+        {
+            try
+            {
+                var response = _endpointApi.SetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationPatchWithHttpInfo(
+                    appId,
+                    endpointId,
+                    endpointTransformationIn,
+                    idempotencyKey);
+
+                return response.StatusCode == HttpStatusCode.NoContent;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(TransformationGet)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
+
+        public async Task<bool> TransformationPartialUpdateAsync(string appId, string endpointId, EndpointTransformationIn endpointTransformationIn, string idempotencyKey = default, CancellationToken cancellationToken = default)
+        {
+            try
+            {
+                var response = await _endpointApi.SetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationPatchWithHttpInfoAsync(
+                    appId,
+                    endpointId,
+                    endpointTransformationIn,
+                    idempotencyKey,
+                    cancellationToken);
+
+                return response.StatusCode == HttpStatusCode.NoContent;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(TransformationPartialUpdateAsync)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
     }
 }
diff --git a/csharp/Svix/EventType.cs b/csharp/Svix/EventType.cs
index fdb7ed428..76c01d98c 100644
--- a/csharp/Svix/EventType.cs
+++ b/csharp/Svix/EventType.cs
@@ -28,6 +28,7 @@ public bool Archive(string eventType, string idempotencyKey = default)
             {
                 var lResponse = _eventTypeApi.DeleteEventTypeApiV1EventTypeEventTypeNameDeleteWithHttpInfo(
                     eventType,
+                    null,
                     idempotencyKey);
 
                 return lResponse.StatusCode == HttpStatusCode.NoContent;
@@ -49,6 +50,7 @@ public async Task<bool> ArchiveAsync(string eventType, string idempotencyKey = d
             {
                 var lResponse = await _eventTypeApi.DeleteEventTypeApiV1EventTypeEventTypeNameDeleteWithHttpInfoAsync(
                     eventType,
+                    null,
                     idempotencyKey,
                     cancellationToken);
 
diff --git a/csharp/Svix/Message.cs b/csharp/Svix/Message.cs
index 1b6fc8ed9..b07ab2a24 100644
--- a/csharp/Svix/Message.cs
+++ b/csharp/Svix/Message.cs
@@ -1,6 +1,7 @@
 ﻿using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Net;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.Extensions.Logging;
@@ -171,5 +172,50 @@ public async Task<List<MessageOut>> ListAsync(string appId, MessageListOptions o
                 return new List<MessageOut>();
             }
         }
+
+        public bool ExpungeContent(string appId, string messageId, string idempotencyKey = default)
+        {
+            try
+            {
+                var lResponse = _messageApi.ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteWithHttpInfo(
+                    messageId,
+                    appId,
+                    idempotencyKey);
+
+                return lResponse.StatusCode == HttpStatusCode.NoContent;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(ExpungeContent)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
+
+        public async Task<bool> ExpungeContentAsync(string appId, string messageId, string idempotencyKey = default, CancellationToken cancellationToken = default)
+        {
+            try
+            {
+                var lResponse = await _messageApi.ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteWithHttpInfoAsync(
+                    messageId,
+                    appId,
+                    idempotencyKey,
+                    cancellationToken);
+
+                return lResponse.StatusCode == HttpStatusCode.NoContent;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(ExpungeContentAsync)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
     }
 }
diff --git a/csharp/Svix/MessageAttempt.cs b/csharp/Svix/MessageAttempt.cs
index be82dedc7..64823aa80 100644
--- a/csharp/Svix/MessageAttempt.cs
+++ b/csharp/Svix/MessageAttempt.cs
@@ -1,4 +1,4 @@
-﻿using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Net;
@@ -82,7 +82,7 @@ public List<EndpointMessageOut> ListAttemptedMessages(string appId, string endpo
                     options?.Iterator,
                     options?.Limit,
                     options?.Channel,
-                    (Svix.Model.MessageStatus)options?.Status,
+                    (Svix.Model.MessageStatus?)options?.Status,
                     options?.Before,
                     options?.After,
                     idempotencyKey);
@@ -111,7 +111,7 @@ public async Task<List<EndpointMessageOut>> ListAttemptedMessagesAsync(string ap
                     options?.Iterator,
                     options?.Limit,
                     options?.Channel,
-                    (Svix.Model.MessageStatus)options?.Status,
+                    (Svix.Model.MessageStatus?)options?.Status,
                     options?.Before,
                     options?.After,
                     idempotencyKey,
@@ -140,8 +140,8 @@ public List<MessageAttemptOut> ListAttemptsByEndpoint(string appId, string endpo
                     endpointId,
                     options?.Iterator,
                     options?.Limit,
-                    (Svix.Model.MessageStatus)options?.Status,
-                    (Svix.Model.StatusCodeClass)options?.Code,
+                    (Svix.Model.MessageStatus?)options?.Status,
+                    (Svix.Model.StatusCodeClass?)options?.Code,
                     options?.EventTypes,
                     options?.Channel,
                     options?.Before,
@@ -171,8 +171,8 @@ public async Task<List<MessageAttemptOut>> ListAttemptsByEndpointAsync(string ap
                     endpointId,
                     options?.Iterator,
                     options?.Limit,
-                    (Svix.Model.MessageStatus)options?.Status,
-                    (Svix.Model.StatusCodeClass)options?.Code,
+                    (Svix.Model.MessageStatus?)options?.Status,
+                    (Svix.Model.StatusCodeClass?)options?.Code,
                     options?.EventTypes,
                     options?.Channel,
                     options?.Before,
@@ -204,8 +204,8 @@ public List<MessageAttemptOut> ListAttemptsByMessage(string appId, string messag
                     options?.EndpointId,
                     options?.Iterator,
                     options?.Limit,
-                    (Svix.Model.MessageStatus)options?.Status,
-                    (Svix.Model.StatusCodeClass)options?.Code,
+                    (Svix.Model.MessageStatus?)options?.Status,
+                    (Svix.Model.StatusCodeClass?)options?.Code,
                     options?.EventTypes,
                     options?.Channel,
                     options?.Before,
@@ -236,8 +236,8 @@ public async Task<List<MessageAttemptOut>> ListAttemptsByMessageAsync(string app
                     options?.EndpointId,
                     options?.Iterator,
                     options?.Limit,
-                    (Svix.Model.MessageStatus)options?.Status,
-                    (Svix.Model.StatusCodeClass)options?.Code,
+                    (Svix.Model.MessageStatus?)options?.Status,
+                    (Svix.Model.StatusCodeClass?)options?.Code,
                     options?.EventTypes,
                     options?.Channel,
                     options?.Before,
@@ -272,7 +272,7 @@ public List<MessageAttemptEndpointOut> ListAttemptsForEndpoint(string appId, str
                     options?.Limit,
                     options?.EventTypes,
                     options?.Channel,
-                    (Svix.Model.MessageStatus)options?.Status,
+                    (Svix.Model.MessageStatus?)options?.Status,
                     options?.Before,
                     options?.After,
                     idempotencyKey);
@@ -305,7 +305,7 @@ public async Task<List<MessageAttemptEndpointOut>> ListAttemptsForEndpointAsync(
                     options?.Limit,
                     options?.EventTypes?.ToList(),
                     options?.Channel,
-                    (Svix.Model.MessageStatus)options?.Status,
+                    (Svix.Model.MessageStatus?)options?.Status,
                     options?.Before,
                     options?.After,
                     idempotencyKey,
@@ -338,7 +338,7 @@ public List<MessageAttemptOut> ListAttempts(string appId, string messageId, Mess
                     options?.EndpointId,
                     options?.EventTypes,
                     options?.Channel,
-                    (Svix.Model.MessageStatus)options?.Status,
+                    (Svix.Model.MessageStatus?)options?.Status,
                     options?.Before,
                     options?.After,
                     idempotencyKey);
@@ -370,7 +370,7 @@ public async Task<List<MessageAttemptOut>> ListAttemptsAsync(string appId, strin
                     options?.EndpointId,
                     options?.EventTypes,
                     options?.Channel,
-                    (Svix.Model.MessageStatus)options?.Status,
+                    (Svix.Model.MessageStatus?)options?.Status,
                     options?.Before,
                     options?.After,
                     idempotencyKey,
@@ -487,5 +487,52 @@ public async Task<bool> ResendWebhookAsync(string appId, string messageId, strin
                 return false;
             }
         }
+
+        public bool ExpungeContent(string appId, string messageId, string attemptId, string idempotencyKey = default)
+        {
+            try
+            {
+                var lResponse = _messageAttemptApi.ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteWithHttpInfo(
+                    attemptId,
+                    messageId,
+                    appId,
+                    idempotencyKey);
+
+                return lResponse.StatusCode == HttpStatusCode.NoContent;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(ExpungeContent)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
+
+        public async Task<bool> ExpungeContentAsync(string appId, string messageId, string attemptId, string idempotencyKey = default, CancellationToken cancellationToken = default)
+        {
+            try
+            {
+                var lResponse = await _messageAttemptApi.ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteWithHttpInfoAsync(
+                    attemptId,
+                    messageId,
+                    appId,
+                    idempotencyKey,
+                    cancellationToken);
+
+                return lResponse.StatusCode == HttpStatusCode.NoContent;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(ExpungeContentAsync)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
     }
 }
diff --git a/csharp/Svix/Models/ListOptions.cs b/csharp/Svix/Models/ListOptions.cs
index d81a56891..02c0e1874 100644
--- a/csharp/Svix/Models/ListOptions.cs
+++ b/csharp/Svix/Models/ListOptions.cs
@@ -1,9 +1,13 @@
-﻿namespace Svix.Models
+﻿using Svix.Model;
+
+namespace Svix.Models
 {
     public class ListOptions
     {
         public string? Iterator { get; set; }
 
         public int? Limit { get; set; }
+
+        public Ordering? Order { get; set; }
     }
 }
\ No newline at end of file
diff --git a/csharp/Svix/Models/SvixOptions.cs b/csharp/Svix/Models/SvixOptions.cs
index fadbec50a..fc2c14775 100644
--- a/csharp/Svix/Models/SvixOptions.cs
+++ b/csharp/Svix/Models/SvixOptions.cs
@@ -13,7 +13,7 @@ private SvixOptions()
             // empty
         }
 
-        public SvixOptions(string serverUrl, bool bThrow = false)
+        public SvixOptions(string serverUrl, bool bThrow = true)
         {
             ServerUrl = serverUrl;
             Throw = bThrow;
diff --git a/csharp/Svix/Svix.csproj b/csharp/Svix/Svix.csproj
index 3abe029e6..ba4485477 100644
--- a/csharp/Svix/Svix.csproj
+++ b/csharp/Svix/Svix.csproj
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <TargetFrameworks>net5.0;netstandard2.0</TargetFrameworks>
     <PackageId>Svix</PackageId>
-    <Version>0.75.0</Version>
+    <Version>0.81.0</Version>
     <Authors>Svix</Authors>
     <Company>Svix</Company>
     <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
diff --git a/go.mod b/go.mod
index 655810daf..e46281561 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,12 @@
 module github.com/svix/svix-webhooks
 
-go 1.16
+go 1.20
 
 require golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
+
+require (
+	github.com/golang/protobuf v1.4.2 // indirect
+	golang.org/x/net v0.7.0 // indirect
+	google.golang.org/appengine v1.6.6 // indirect
+	google.golang.org/protobuf v1.25.0 // indirect
+)
diff --git a/go.sum b/go.sum
index ab0770aff..40be1e6a2 100644
--- a/go.sum
+++ b/go.sum
@@ -176,8 +176,9 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/
 golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
diff --git a/go/application.go b/go/application.go
index 678de412a..2746bee5c 100644
--- a/go/application.go
+++ b/go/application.go
@@ -19,10 +19,11 @@ type Application struct {
 type ApplicationListOptions struct {
 	Iterator *string
 	Limit    *int32
+	Order    *Ordering
 }
 
-func (a *Application) List(options *ApplicationListOptions) (*ListResponseApplicationOut, error) {
-	req := a.api.ApplicationApi.ListApplicationsApiV1AppGet(context.Background())
+func (a *Application) List(ctx context.Context, options *ApplicationListOptions) (*ListResponseApplicationOut, error) {
+	req := a.api.ApplicationApi.ListApplicationsApiV1AppGet(ctx)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -30,6 +31,9 @@ func (a *Application) List(options *ApplicationListOptions) (*ListResponseApplic
 		if options.Limit != nil {
 			req = req.Limit(*options.Limit)
 		}
+		if options.Order != nil {
+			req = req.Order(openapi.Ordering(*options.Order))
+		}
 	}
 	resp, res, err := req.Execute()
 	if err != nil {
@@ -39,12 +43,12 @@ func (a *Application) List(options *ApplicationListOptions) (*ListResponseApplic
 	return &ret, nil
 }
 
-func (a *Application) Create(applicationIn *ApplicationIn) (*ApplicationOut, error) {
-	return a.CreateWithOptions(applicationIn, nil)
+func (a *Application) Create(ctx context.Context, applicationIn *ApplicationIn) (*ApplicationOut, error) {
+	return a.CreateWithOptions(ctx, applicationIn, nil)
 }
 
-func (a *Application) CreateWithOptions(applicationIn *ApplicationIn, options *PostOptions) (*ApplicationOut, error) {
-	req := a.api.ApplicationApi.CreateApplicationApiV1AppPost(context.Background())
+func (a *Application) CreateWithOptions(ctx context.Context, applicationIn *ApplicationIn, options *PostOptions) (*ApplicationOut, error) {
+	req := a.api.ApplicationApi.CreateApplicationApiV1AppPost(ctx)
 	req = req.ApplicationIn(openapi.ApplicationIn(*applicationIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -59,12 +63,12 @@ func (a *Application) CreateWithOptions(applicationIn *ApplicationIn, options *P
 	return &ret, nil
 }
 
-func (a *Application) GetOrCreate(applicationIn *ApplicationIn) (*ApplicationOut, error) {
-	return a.GetOrCreateWithOptions(applicationIn, nil)
+func (a *Application) GetOrCreate(ctx context.Context, applicationIn *ApplicationIn) (*ApplicationOut, error) {
+	return a.GetOrCreateWithOptions(ctx, applicationIn, nil)
 }
 
-func (a *Application) GetOrCreateWithOptions(applicationIn *ApplicationIn, options *PostOptions) (*ApplicationOut, error) {
-	req := a.api.ApplicationApi.CreateApplicationApiV1AppPost(context.Background())
+func (a *Application) GetOrCreateWithOptions(ctx context.Context, applicationIn *ApplicationIn, options *PostOptions) (*ApplicationOut, error) {
+	req := a.api.ApplicationApi.CreateApplicationApiV1AppPost(ctx)
 	req = req.ApplicationIn(openapi.ApplicationIn(*applicationIn))
 	req = req.GetIfExists(true)
 	if options != nil {
@@ -80,8 +84,8 @@ func (a *Application) GetOrCreateWithOptions(applicationIn *ApplicationIn, optio
 	return &ret, nil
 }
 
-func (a *Application) Get(appId string) (*ApplicationOut, error) {
-	req := a.api.ApplicationApi.GetApplicationApiV1AppAppIdGet(context.Background(), appId)
+func (a *Application) Get(ctx context.Context, appId string) (*ApplicationOut, error) {
+	req := a.api.ApplicationApi.GetApplicationApiV1AppAppIdGet(ctx, appId)
 	resp, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -90,8 +94,8 @@ func (a *Application) Get(appId string) (*ApplicationOut, error) {
 	return &ret, nil
 }
 
-func (a *Application) Update(appId string, applicationIn *ApplicationIn) (*ApplicationOut, error) {
-	req := a.api.ApplicationApi.UpdateApplicationApiV1AppAppIdPut(context.Background(), appId)
+func (a *Application) Update(ctx context.Context, appId string, applicationIn *ApplicationIn) (*ApplicationOut, error) {
+	req := a.api.ApplicationApi.UpdateApplicationApiV1AppAppIdPut(ctx, appId)
 	req = req.ApplicationIn(openapi.ApplicationIn(*applicationIn))
 	resp, res, err := req.Execute()
 	if err != nil {
@@ -101,8 +105,8 @@ func (a *Application) Update(appId string, applicationIn *ApplicationIn) (*Appli
 	return &ret, nil
 }
 
-func (a *Application) Delete(appId string) error {
-	req := a.api.ApplicationApi.DeleteApplicationApiV1AppAppIdDelete(context.Background(), appId)
+func (a *Application) Delete(ctx context.Context, appId string) error {
+	req := a.api.ApplicationApi.DeleteApplicationApiV1AppAppIdDelete(ctx, appId)
 	res, err := req.Execute()
 	return wrapError(err, res)
 }
diff --git a/go/authentication.go b/go/authentication.go
index 49d962112..a056da416 100644
--- a/go/authentication.go
+++ b/go/authentication.go
@@ -16,12 +16,12 @@ type (
 	DashboardAccessOut openapi.DashboardAccessOut
 )
 
-func (a *Authentication) AppPortalAccess(appId string, appPortalAccessIn *AppPortalAccessIn) (*AppPortalAccessOut, error) {
-	return a.AppPortalAccessWithOptions(appId, appPortalAccessIn, nil)
+func (a *Authentication) AppPortalAccess(ctx context.Context, appId string, appPortalAccessIn *AppPortalAccessIn) (*AppPortalAccessOut, error) {
+	return a.AppPortalAccessWithOptions(ctx, appId, appPortalAccessIn, nil)
 }
 
-func (a *Authentication) AppPortalAccessWithOptions(appId string, appPortalAccessIn *AppPortalAccessIn, options *PostOptions) (*AppPortalAccessOut, error) {
-	req := a.api.AuthenticationApi.GetAppPortalAccessApiV1AuthAppPortalAccessAppIdPost(context.Background(), appId)
+func (a *Authentication) AppPortalAccessWithOptions(ctx context.Context, appId string, appPortalAccessIn *AppPortalAccessIn, options *PostOptions) (*AppPortalAccessOut, error) {
+	req := a.api.AuthenticationApi.GetAppPortalAccessApiV1AuthAppPortalAccessAppIdPost(ctx, appId)
 	req = req.AppPortalAccessIn(openapi.AppPortalAccessIn(*appPortalAccessIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -36,12 +36,12 @@ func (a *Authentication) AppPortalAccessWithOptions(appId string, appPortalAcces
 	return &ret, nil
 }
 
-func (a *Authentication) DashboardAccess(appId string) (*DashboardAccessOut, error) {
-	return a.DashboardAccessWithOptions(appId, nil)
+func (a *Authentication) DashboardAccess(ctx context.Context, appId string) (*DashboardAccessOut, error) {
+	return a.DashboardAccessWithOptions(ctx, appId, nil)
 }
 
-func (a *Authentication) DashboardAccessWithOptions(appId string, options *PostOptions) (*DashboardAccessOut, error) {
-	req := a.api.AuthenticationApi.GetDashboardAccessApiV1AuthDashboardAccessAppIdPost(context.Background(), appId)
+func (a *Authentication) DashboardAccessWithOptions(ctx context.Context, appId string, options *PostOptions) (*DashboardAccessOut, error) {
+	req := a.api.AuthenticationApi.GetDashboardAccessApiV1AuthDashboardAccessAppIdPost(ctx, appId)
 	if options != nil {
 		if options.IdempotencyKey != nil {
 			req = req.IdempotencyKey(*options.IdempotencyKey)
@@ -55,12 +55,12 @@ func (a *Authentication) DashboardAccessWithOptions(appId string, options *PostO
 	return &ret, nil
 }
 
-func (a *Authentication) Logout() error {
-	return a.LogoutWithOptions(nil)
+func (a *Authentication) Logout(ctx context.Context) error {
+	return a.LogoutWithOptions(ctx, nil)
 }
 
-func (a *Authentication) LogoutWithOptions(options *PostOptions) error {
-	req := a.api.AuthenticationApi.LogoutApiV1AuthLogoutPost(context.Background())
+func (a *Authentication) LogoutWithOptions(ctx context.Context, options *PostOptions) error {
+	req := a.api.AuthenticationApi.LogoutApiV1AuthLogoutPost(ctx)
 	if options != nil {
 		if options.IdempotencyKey != nil {
 			req = req.IdempotencyKey(*options.IdempotencyKey)
diff --git a/go/endpoint.go b/go/endpoint.go
index fe066bb87..10e520787 100644
--- a/go/endpoint.go
+++ b/go/endpoint.go
@@ -7,17 +7,21 @@ import (
 )
 
 type (
-	ListResponseEndpointOut openapi.ListResponseEndpointOut
-	EndpointIn              openapi.EndpointIn
-	EndpointUpdate          openapi.EndpointUpdate
-	EndpointOut             openapi.EndpointOut
-	EndpointSecretOut       openapi.EndpointSecretOut
-	EndpointSecretRotateIn  openapi.EndpointSecretRotateIn
-	RecoverIn               openapi.RecoverIn
-	EndpointHeadersIn       openapi.EndpointHeadersIn
-	EndpointHeadersPatchIn  openapi.EndpointHeadersPatchIn
-	EndpointHeadersOut      openapi.EndpointHeadersOut
-	EndpointStats			openapi.EndpointStats
+	ListResponseEndpointOut   openapi.ListResponseEndpointOut
+	EndpointIn                openapi.EndpointIn
+	EndpointUpdate            openapi.EndpointUpdate
+	EndpointOut               openapi.EndpointOut
+	EndpointSecretOut         openapi.EndpointSecretOut
+	EndpointSecretRotateIn    openapi.EndpointSecretRotateIn
+	EndpointTransformationIn  openapi.EndpointTransformationIn
+	RecoverIn                 openapi.RecoverIn
+	ReplayIn                  openapi.ReplayIn
+	EndpointHeadersIn         openapi.EndpointHeadersIn
+	EndpointHeadersPatchIn    openapi.EndpointHeadersPatchIn
+	EndpointHeadersOut        openapi.EndpointHeadersOut
+	EndpointStats             openapi.EndpointStats
+	EndpointTransformationOut openapi.EndpointTransformationOut
+	Ordering                  openapi.Ordering
 )
 
 type Endpoint struct {
@@ -27,10 +31,11 @@ type Endpoint struct {
 type EndpointListOptions struct {
 	Iterator *string
 	Limit    *int32
+	Order    *Ordering
 }
 
-func (e *Endpoint) List(appId string, options *EndpointListOptions) (*ListResponseEndpointOut, error) {
-	req := e.api.EndpointApi.ListEndpointsApiV1AppAppIdEndpointGet(context.Background(), appId)
+func (e *Endpoint) List(ctx context.Context, appId string, options *EndpointListOptions) (*ListResponseEndpointOut, error) {
+	req := e.api.EndpointApi.ListEndpointsApiV1AppAppIdEndpointGet(ctx, appId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -38,6 +43,9 @@ func (e *Endpoint) List(appId string, options *EndpointListOptions) (*ListRespon
 		if options.Limit != nil {
 			req = req.Limit(*options.Limit)
 		}
+		if options.Order != nil {
+			req = req.Order(openapi.Ordering(*options.Order))
+		}
 	}
 	out, res, err := req.Execute()
 	if err != nil {
@@ -47,12 +55,12 @@ func (e *Endpoint) List(appId string, options *EndpointListOptions) (*ListRespon
 	return &ret, nil
 }
 
-func (e *Endpoint) Create(appId string, endpointIn *EndpointIn) (*EndpointOut, error) {
-	return e.CreateWithOptions(appId, endpointIn, nil)
+func (e *Endpoint) Create(ctx context.Context, appId string, endpointIn *EndpointIn) (*EndpointOut, error) {
+	return e.CreateWithOptions(ctx, appId, endpointIn, nil)
 }
 
-func (e *Endpoint) CreateWithOptions(appId string, endpointIn *EndpointIn, options *PostOptions) (*EndpointOut, error) {
-	req := e.api.EndpointApi.CreateEndpointApiV1AppAppIdEndpointPost(context.Background(), appId)
+func (e *Endpoint) CreateWithOptions(ctx context.Context, appId string, endpointIn *EndpointIn, options *PostOptions) (*EndpointOut, error) {
+	req := e.api.EndpointApi.CreateEndpointApiV1AppAppIdEndpointPost(ctx, appId)
 	req = req.EndpointIn(openapi.EndpointIn(*endpointIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -67,8 +75,8 @@ func (e *Endpoint) CreateWithOptions(appId string, endpointIn *EndpointIn, optio
 	return &ret, nil
 }
 
-func (e *Endpoint) Get(appId string, endpointId string) (*EndpointOut, error) {
-	req := e.api.EndpointApi.GetEndpointApiV1AppAppIdEndpointEndpointIdGet(context.Background(), endpointId, appId)
+func (e *Endpoint) Get(ctx context.Context, appId string, endpointId string) (*EndpointOut, error) {
+	req := e.api.EndpointApi.GetEndpointApiV1AppAppIdEndpointEndpointIdGet(ctx, endpointId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -77,8 +85,8 @@ func (e *Endpoint) Get(appId string, endpointId string) (*EndpointOut, error) {
 	return &ret, nil
 }
 
-func (e *Endpoint) Update(appId string, endpointId string, endpointUpdate *EndpointUpdate) (*EndpointOut, error) {
-	req := e.api.EndpointApi.UpdateEndpointApiV1AppAppIdEndpointEndpointIdPut(context.Background(), endpointId, appId)
+func (e *Endpoint) Update(ctx context.Context, appId string, endpointId string, endpointUpdate *EndpointUpdate) (*EndpointOut, error) {
+	req := e.api.EndpointApi.UpdateEndpointApiV1AppAppIdEndpointEndpointIdPut(ctx, endpointId, appId)
 	req = req.EndpointUpdate(openapi.EndpointUpdate(*endpointUpdate))
 	out, res, err := req.Execute()
 	if err != nil {
@@ -88,14 +96,14 @@ func (e *Endpoint) Update(appId string, endpointId string, endpointUpdate *Endpo
 	return &ret, nil
 }
 
-func (e *Endpoint) Delete(appId string, endpointId string) error {
-	req := e.api.EndpointApi.DeleteEndpointApiV1AppAppIdEndpointEndpointIdDelete(context.Background(), endpointId, appId)
+func (e *Endpoint) Delete(ctx context.Context, appId string, endpointId string) error {
+	req := e.api.EndpointApi.DeleteEndpointApiV1AppAppIdEndpointEndpointIdDelete(ctx, endpointId, appId)
 	res, err := req.Execute()
 	return wrapError(err, res)
 }
 
-func (e *Endpoint) GetSecret(appId string, endpointId string) (*EndpointSecretOut, error) {
-	req := e.api.EndpointApi.GetEndpointSecretApiV1AppAppIdEndpointEndpointIdSecretGet(context.Background(), endpointId, appId)
+func (e *Endpoint) GetSecret(ctx context.Context, appId string, endpointId string) (*EndpointSecretOut, error) {
+	req := e.api.EndpointApi.GetEndpointSecretApiV1AppAppIdEndpointEndpointIdSecretGet(ctx, endpointId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -104,12 +112,12 @@ func (e *Endpoint) GetSecret(appId string, endpointId string) (*EndpointSecretOu
 	return &ret, nil
 }
 
-func (e *Endpoint) RotateSecret(appId string, endpointId string, endpointSecretRotateIn *EndpointSecretRotateIn) error {
-	return e.RotateSecretWithOptions(appId, endpointId, endpointSecretRotateIn, nil)
+func (e *Endpoint) RotateSecret(ctx context.Context, appId string, endpointId string, endpointSecretRotateIn *EndpointSecretRotateIn) error {
+	return e.RotateSecretWithOptions(ctx, appId, endpointId, endpointSecretRotateIn, nil)
 }
 
-func (e *Endpoint) RotateSecretWithOptions(appId string, endpointId string, endpointSecretRotateIn *EndpointSecretRotateIn, options *PostOptions) error {
-	req := e.api.EndpointApi.RotateEndpointSecretApiV1AppAppIdEndpointEndpointIdSecretRotatePost(context.Background(), endpointId, appId)
+func (e *Endpoint) RotateSecretWithOptions(ctx context.Context, appId string, endpointId string, endpointSecretRotateIn *EndpointSecretRotateIn, options *PostOptions) error {
+	req := e.api.EndpointApi.RotateEndpointSecretApiV1AppAppIdEndpointEndpointIdSecretRotatePost(ctx, endpointId, appId)
 	req = req.EndpointSecretRotateIn(openapi.EndpointSecretRotateIn(*endpointSecretRotateIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -123,12 +131,12 @@ func (e *Endpoint) RotateSecretWithOptions(appId string, endpointId string, endp
 	return nil
 }
 
-func (e *Endpoint) Recover(appId string, endpointId string, recoverIn *RecoverIn) error {
-	return e.RecoverWithOptions(appId, endpointId, recoverIn, nil)
+func (e *Endpoint) Recover(ctx context.Context, appId string, endpointId string, recoverIn *RecoverIn) error {
+	return e.RecoverWithOptions(ctx, appId, endpointId, recoverIn, nil)
 }
 
-func (e *Endpoint) RecoverWithOptions(appId string, endpointId string, recoverIn *RecoverIn, options *PostOptions) error {
-	req := e.api.EndpointApi.RecoverFailedWebhooksApiV1AppAppIdEndpointEndpointIdRecoverPost(context.Background(), appId, endpointId)
+func (e *Endpoint) RecoverWithOptions(ctx context.Context, appId string, endpointId string, recoverIn *RecoverIn, options *PostOptions) error {
+	req := e.api.EndpointApi.RecoverFailedWebhooksApiV1AppAppIdEndpointEndpointIdRecoverPost(ctx, appId, endpointId)
 	req = req.RecoverIn(openapi.RecoverIn(*recoverIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -142,8 +150,8 @@ func (e *Endpoint) RecoverWithOptions(appId string, endpointId string, recoverIn
 	return nil
 }
 
-func (e *Endpoint) GetHeaders(appId string, endpointId string) (*EndpointHeadersOut, error) {
-	req := e.api.EndpointApi.GetEndpointHeadersApiV1AppAppIdEndpointEndpointIdHeadersGet(context.Background(), endpointId, appId)
+func (e *Endpoint) GetHeaders(ctx context.Context, appId string, endpointId string) (*EndpointHeadersOut, error) {
+	req := e.api.EndpointApi.GetEndpointHeadersApiV1AppAppIdEndpointEndpointIdHeadersGet(ctx, endpointId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -152,8 +160,8 @@ func (e *Endpoint) GetHeaders(appId string, endpointId string) (*EndpointHeaders
 	return &ret, nil
 }
 
-func (e *Endpoint) UpdateHeaders(appId string, endpointId string, endpointHeadersIn *EndpointHeadersIn) error {
-	req := e.api.EndpointApi.UpdateEndpointHeadersApiV1AppAppIdEndpointEndpointIdHeadersPut(context.Background(), appId, endpointId)
+func (e *Endpoint) UpdateHeaders(ctx context.Context, appId string, endpointId string, endpointHeadersIn *EndpointHeadersIn) error {
+	req := e.api.EndpointApi.UpdateEndpointHeadersApiV1AppAppIdEndpointEndpointIdHeadersPut(ctx, appId, endpointId)
 	req = req.EndpointHeadersIn(openapi.EndpointHeadersIn(*endpointHeadersIn))
 	res, err := req.Execute()
 	if err != nil {
@@ -162,8 +170,8 @@ func (e *Endpoint) UpdateHeaders(appId string, endpointId string, endpointHeader
 	return nil
 }
 
-func (e *Endpoint) PatchHeaders(appId string, endpointId string, endpointHeadersIn *EndpointHeadersPatchIn) error {
-	req := e.api.EndpointApi.PatchEndpointHeadersApiV1AppAppIdEndpointEndpointIdHeadersPatch(context.Background(), appId, endpointId)
+func (e *Endpoint) PatchHeaders(ctx context.Context, appId string, endpointId string, endpointHeadersIn *EndpointHeadersPatchIn) error {
+	req := e.api.EndpointApi.PatchEndpointHeadersApiV1AppAppIdEndpointEndpointIdHeadersPatch(ctx, appId, endpointId)
 	req = req.EndpointHeadersPatchIn(openapi.EndpointHeadersPatchIn(*endpointHeadersIn))
 	res, err := req.Execute()
 	if err != nil {
@@ -172,8 +180,8 @@ func (e *Endpoint) PatchHeaders(appId string, endpointId string, endpointHeaders
 	return nil
 }
 
-func (e *Endpoint) GetStats(appId string, endpointId string) (*EndpointStats, error) {
-	req := e.api.EndpointApi.GetEndpointStatsApiV1AppAppIdEndpointEndpointIdStatsGet(context.Background(), endpointId, appId)
+func (e *Endpoint) GetStats(ctx context.Context, appId string, endpointId string) (*EndpointStats, error) {
+	req := e.api.EndpointApi.GetEndpointStatsApiV1AppAppIdEndpointEndpointIdStatsGet(ctx, endpointId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -181,3 +189,52 @@ func (e *Endpoint) GetStats(appId string, endpointId string) (*EndpointStats, er
 	ret := EndpointStats(out)
 	return &ret, nil
 }
+
+func (e *Endpoint) ReplayMissing(ctx context.Context, appId string, endpointId string, replayIn *ReplayIn) error {
+	return e.ReplayMissingWithOptions(ctx, appId, endpointId, replayIn, nil)
+}
+
+func (e *Endpoint) ReplayMissingWithOptions(
+	ctx context.Context,
+	appId string,
+	endpointId string,
+	replayIn *ReplayIn,
+	options *PostOptions,
+) error {
+	req := e.api.EndpointApi.ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPost(ctx, appId, endpointId)
+	req.ReplayIn(openapi.ReplayIn(*replayIn))
+	if options != nil {
+		if options.IdempotencyKey != nil {
+			req = req.IdempotencyKey(*options.IdempotencyKey)
+		}
+	}
+	_, res, err := req.Execute()
+	if err != nil {
+		return wrapError(err, res)
+	}
+	return nil
+}
+
+func (e *Endpoint) TransformationGet(ctx context.Context, appId string, endpointId string) (*EndpointTransformationOut, error) {
+	req := e.api.EndpointApi.GetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationGet(ctx, endpointId, appId)
+
+	out, res, err := req.Execute()
+	if err != nil {
+		return nil, wrapError(err, res)
+	}
+
+	ret := EndpointTransformationOut(out)
+	return &ret, nil
+}
+
+func (e *Endpoint) TransformatioPartialUpdate(ctx context.Context, appId string, endpointId string, transformation *EndpointTransformationIn) error {
+	req := e.api.EndpointApi.SetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationPatch(ctx, appId, endpointId)
+	req.EndpointTransformationIn(openapi.EndpointTransformationIn(*transformation))
+
+	res, err := req.Execute()
+	if err != nil {
+		return wrapError(err, res)
+	}
+
+	return nil
+}
diff --git a/go/eventtype.go b/go/eventtype.go
index 61ea2736a..288c49803 100644
--- a/go/eventtype.go
+++ b/go/eventtype.go
@@ -24,8 +24,8 @@ type EventTypeListOptions struct {
 	IncludeArchived *bool
 }
 
-func (e *EventType) List(options *EventTypeListOptions) (*ListResponseEventTypeOut, error) {
-	req := e.api.EventTypeApi.ListEventTypesApiV1EventTypeGet(context.Background())
+func (e *EventType) List(ctx context.Context, options *EventTypeListOptions) (*ListResponseEventTypeOut, error) {
+	req := e.api.EventTypeApi.ListEventTypesApiV1EventTypeGet(ctx)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -48,12 +48,12 @@ func (e *EventType) List(options *EventTypeListOptions) (*ListResponseEventTypeO
 	return &ret, nil
 }
 
-func (e *EventType) Create(eventTypeIn *EventTypeIn) (*EventTypeOut, error) {
-	return e.CreateWithOptions(eventTypeIn, nil)
+func (e *EventType) Create(ctx context.Context, eventTypeIn *EventTypeIn) (*EventTypeOut, error) {
+	return e.CreateWithOptions(ctx, eventTypeIn, nil)
 }
 
-func (e *EventType) CreateWithOptions(eventTypeIn *EventTypeIn, options *PostOptions) (*EventTypeOut, error) {
-	req := e.api.EventTypeApi.CreateEventTypeApiV1EventTypePost(context.Background())
+func (e *EventType) CreateWithOptions(ctx context.Context, eventTypeIn *EventTypeIn, options *PostOptions) (*EventTypeOut, error) {
+	req := e.api.EventTypeApi.CreateEventTypeApiV1EventTypePost(ctx)
 	req = req.EventTypeIn(openapi.EventTypeIn(*eventTypeIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -68,8 +68,8 @@ func (e *EventType) CreateWithOptions(eventTypeIn *EventTypeIn, options *PostOpt
 	return &ret, nil
 }
 
-func (e *EventType) Get(eventTypeName string) (*EventTypeOut, error) {
-	req := e.api.EventTypeApi.GetEventTypeApiV1EventTypeEventTypeNameGet(context.Background(), eventTypeName)
+func (e *EventType) Get(ctx context.Context, eventTypeName string) (*EventTypeOut, error) {
+	req := e.api.EventTypeApi.GetEventTypeApiV1EventTypeEventTypeNameGet(ctx, eventTypeName)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -78,8 +78,8 @@ func (e *EventType) Get(eventTypeName string) (*EventTypeOut, error) {
 	return &ret, nil
 }
 
-func (e *EventType) Update(eventTypeName string, eventTypeUpdate *EventTypeUpdate) (*EventTypeOut, error) {
-	req := e.api.EventTypeApi.UpdateEventTypeApiV1EventTypeEventTypeNamePut(context.Background(), eventTypeName)
+func (e *EventType) Update(ctx context.Context, eventTypeName string, eventTypeUpdate *EventTypeUpdate) (*EventTypeOut, error) {
+	req := e.api.EventTypeApi.UpdateEventTypeApiV1EventTypeEventTypeNamePut(ctx, eventTypeName)
 	req = req.EventTypeUpdate(openapi.EventTypeUpdate(*eventTypeUpdate))
 	out, res, err := req.Execute()
 	if err != nil {
@@ -89,8 +89,8 @@ func (e *EventType) Update(eventTypeName string, eventTypeUpdate *EventTypeUpdat
 	return &ret, nil
 }
 
-func (e *EventType) Delete(eventTypeName string) error {
-	req := e.api.EventTypeApi.DeleteEventTypeApiV1EventTypeEventTypeNameDelete(context.Background(), eventTypeName)
+func (e *EventType) Delete(ctx context.Context, eventTypeName string) error {
+	req := e.api.EventTypeApi.DeleteEventTypeApiV1EventTypeEventTypeNameDelete(ctx, eventTypeName)
 	res, err := req.Execute()
 	return wrapError(err, res)
 }
diff --git a/go/integration.go b/go/integration.go
index a5a37816b..707d42259 100644
--- a/go/integration.go
+++ b/go/integration.go
@@ -23,8 +23,8 @@ type IntegrationListOptions struct {
 	Limit    *int32
 }
 
-func (e *Integration) List(appId string, options *IntegrationListOptions) (*ListResponseIntegrationOut, error) {
-	req := e.api.IntegrationApi.ListIntegrationsApiV1AppAppIdIntegrationGet(context.Background(), appId)
+func (e *Integration) List(ctx context.Context, appId string, options *IntegrationListOptions) (*ListResponseIntegrationOut, error) {
+	req := e.api.IntegrationApi.ListIntegrationsApiV1AppAppIdIntegrationGet(ctx, appId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -41,12 +41,12 @@ func (e *Integration) List(appId string, options *IntegrationListOptions) (*List
 	return &ret, nil
 }
 
-func (e *Integration) Create(appId string, endpointIn *IntegrationIn) (*IntegrationOut, error) {
-	return e.CreateWithOptions(appId, endpointIn, nil)
+func (e *Integration) Create(ctx context.Context, appId string, endpointIn *IntegrationIn) (*IntegrationOut, error) {
+	return e.CreateWithOptions(ctx, appId, endpointIn, nil)
 }
 
-func (e *Integration) CreateWithOptions(appId string, endpointIn *IntegrationIn, options *PostOptions) (*IntegrationOut, error) {
-	req := e.api.IntegrationApi.CreateIntegrationApiV1AppAppIdIntegrationPost(context.Background(), appId)
+func (e *Integration) CreateWithOptions(ctx context.Context, appId string, endpointIn *IntegrationIn, options *PostOptions) (*IntegrationOut, error) {
+	req := e.api.IntegrationApi.CreateIntegrationApiV1AppAppIdIntegrationPost(ctx, appId)
 	req = req.IntegrationIn(openapi.IntegrationIn(*endpointIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -61,8 +61,8 @@ func (e *Integration) CreateWithOptions(appId string, endpointIn *IntegrationIn,
 	return &ret, nil
 }
 
-func (e *Integration) Get(appId string, integId string) (*IntegrationOut, error) {
-	req := e.api.IntegrationApi.GetIntegrationApiV1AppAppIdIntegrationIntegIdGet(context.Background(), integId, appId)
+func (e *Integration) Get(ctx context.Context, appId string, integId string) (*IntegrationOut, error) {
+	req := e.api.IntegrationApi.GetIntegrationApiV1AppAppIdIntegrationIntegIdGet(ctx, integId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -71,8 +71,8 @@ func (e *Integration) Get(appId string, integId string) (*IntegrationOut, error)
 	return &ret, nil
 }
 
-func (e *Integration) Update(appId string, integId string, endpointUpdate *IntegrationUpdate) (*IntegrationOut, error) {
-	req := e.api.IntegrationApi.UpdateIntegrationApiV1AppAppIdIntegrationIntegIdPut(context.Background(), integId, appId)
+func (e *Integration) Update(ctx context.Context, appId string, integId string, endpointUpdate *IntegrationUpdate) (*IntegrationOut, error) {
+	req := e.api.IntegrationApi.UpdateIntegrationApiV1AppAppIdIntegrationIntegIdPut(ctx, integId, appId)
 	req = req.IntegrationUpdate(openapi.IntegrationUpdate(*endpointUpdate))
 	out, res, err := req.Execute()
 	if err != nil {
@@ -82,14 +82,14 @@ func (e *Integration) Update(appId string, integId string, endpointUpdate *Integ
 	return &ret, nil
 }
 
-func (e *Integration) Delete(appId string, integId string) error {
-	req := e.api.IntegrationApi.DeleteIntegrationApiV1AppAppIdIntegrationIntegIdDelete(context.Background(), integId, appId)
+func (e *Integration) Delete(ctx context.Context, appId string, integId string) error {
+	req := e.api.IntegrationApi.DeleteIntegrationApiV1AppAppIdIntegrationIntegIdDelete(ctx, integId, appId)
 	res, err := req.Execute()
 	return wrapError(err, res)
 }
 
-func (e *Integration) GetKey(appId string, integId string) (*IntegrationKeyOut, error) {
-	req := e.api.IntegrationApi.GetIntegrationKeyApiV1AppAppIdIntegrationIntegIdKeyGet(context.Background(), integId, appId)
+func (e *Integration) GetKey(ctx context.Context, appId string, integId string) (*IntegrationKeyOut, error) {
+	req := e.api.IntegrationApi.GetIntegrationKeyApiV1AppAppIdIntegrationIntegIdKeyGet(ctx, integId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -98,12 +98,12 @@ func (e *Integration) GetKey(appId string, integId string) (*IntegrationKeyOut,
 	return &ret, nil
 }
 
-func (e *Integration) RotateKey(appId string, integId string) (*IntegrationKeyOut, error) {
-	return e.RotateKeyWithOptions(appId, integId, nil)
+func (e *Integration) RotateKey(ctx context.Context, appId string, integId string) (*IntegrationKeyOut, error) {
+	return e.RotateKeyWithOptions(ctx, appId, integId, nil)
 }
 
-func (e *Integration) RotateKeyWithOptions(appId string, integId string, options *PostOptions) (*IntegrationKeyOut, error) {
-	req := e.api.IntegrationApi.RotateIntegrationKeyApiV1AppAppIdIntegrationIntegIdKeyRotatePost(context.Background(), integId, appId)
+func (e *Integration) RotateKeyWithOptions(ctx context.Context, appId string, integId string, options *PostOptions) (*IntegrationKeyOut, error) {
+	req := e.api.IntegrationApi.RotateIntegrationKeyApiV1AppAppIdIntegrationIntegIdKeyRotatePost(ctx, integId, appId)
 	if options != nil {
 		if options.IdempotencyKey != nil {
 			req = req.IdempotencyKey(*options.IdempotencyKey)
diff --git a/go/internal/openapi/api_application.go b/go/internal/openapi/api_application.go
index da0ae6e76..d71a5044e 100644
--- a/go/internal/openapi/api_application.go
+++ b/go/internal/openapi/api_application.go
@@ -967,6 +967,7 @@ type ApiListApplicationsApiV1AppGetRequest struct {
 	ApiService *ApplicationApiService
 	iterator *string
 	limit *int32
+	order *Ordering
 	idempotencyKey *string
 }
 
@@ -978,6 +979,10 @@ func (r ApiListApplicationsApiV1AppGetRequest) Limit(limit int32) ApiListApplica
 	r.limit = &limit
 	return r
 }
+func (r ApiListApplicationsApiV1AppGetRequest) Order(order Ordering) ApiListApplicationsApiV1AppGetRequest {
+	r.order = &order
+	return r
+}
 func (r ApiListApplicationsApiV1AppGetRequest) IdempotencyKey(idempotencyKey string) ApiListApplicationsApiV1AppGetRequest {
 	r.idempotencyKey = &idempotencyKey
 	return r
@@ -1031,6 +1036,9 @@ func (a *ApplicationApiService) ListApplicationsApiV1AppGetExecute(r ApiListAppl
 	if r.limit != nil {
 		localVarQueryParams.Add("limit", parameterToString(*r.limit, ""))
 	}
+	if r.order != nil {
+		localVarQueryParams.Add("order", parameterToString(*r.order, ""))
+	}
 	// to determine the Content-Type header
 	localVarHTTPContentTypes := []string{}
 
diff --git a/go/internal/openapi/api_authentication.go b/go/internal/openapi/api_authentication.go
index 83fb50f6b..295fc4d3e 100644
--- a/go/internal/openapi/api_authentication.go
+++ b/go/internal/openapi/api_authentication.go
@@ -411,7 +411,7 @@ func (r ApiGetDashboardAccessApiV1AuthDashboardAccessAppIdPostRequest) Execute()
 }
 
 /*
- * GetDashboardAccessApiV1AuthDashboardAccessAppIdPost Get Consumer App Portal Access
+ * GetDashboardAccessApiV1AuthDashboardAccessAppIdPost Get Dashboard Access
  * DEPRECATED: Please use `app-portal-access` instead.
 
 Use this function to get magic links (and authentication codes) for connecting your users to the Consumer Application Portal.
diff --git a/go/internal/openapi/api_endpoint.go b/go/internal/openapi/api_endpoint.go
index 19594695b..b2aa81b2d 100644
--- a/go/internal/openapi/api_endpoint.go
+++ b/go/internal/openapi/api_endpoint.go
@@ -1350,6 +1350,7 @@ type ApiListEndpointsApiV1AppAppIdEndpointGetRequest struct {
 	appId string
 	iterator *string
 	limit *int32
+	order *Ordering
 	idempotencyKey *string
 }
 
@@ -1361,6 +1362,10 @@ func (r ApiListEndpointsApiV1AppAppIdEndpointGetRequest) Limit(limit int32) ApiL
 	r.limit = &limit
 	return r
 }
+func (r ApiListEndpointsApiV1AppAppIdEndpointGetRequest) Order(order Ordering) ApiListEndpointsApiV1AppAppIdEndpointGetRequest {
+	r.order = &order
+	return r
+}
 func (r ApiListEndpointsApiV1AppAppIdEndpointGetRequest) IdempotencyKey(idempotencyKey string) ApiListEndpointsApiV1AppAppIdEndpointGetRequest {
 	r.idempotencyKey = &idempotencyKey
 	return r
@@ -1423,6 +1428,9 @@ func (a *EndpointApiService) ListEndpointsApiV1AppAppIdEndpointGetExecute(r ApiL
 	if r.limit != nil {
 		localVarQueryParams.Add("limit", parameterToString(*r.limit, ""))
 	}
+	if r.order != nil {
+		localVarQueryParams.Add("order", parameterToString(*r.order, ""))
+	}
 	// to determine the Content-Type header
 	localVarHTTPContentTypes := []string{}
 
diff --git a/go/internal/openapi/api_event_type.go b/go/internal/openapi/api_event_type.go
index 94dc4de2b..2b4aba51e 100644
--- a/go/internal/openapi/api_event_type.go
+++ b/go/internal/openapi/api_event_type.go
@@ -214,9 +214,14 @@ type ApiDeleteEventTypeApiV1EventTypeEventTypeNameDeleteRequest struct {
 	ctx _context.Context
 	ApiService *EventTypeApiService
 	eventTypeName string
+	expunge *bool
 	idempotencyKey *string
 }
 
+func (r ApiDeleteEventTypeApiV1EventTypeEventTypeNameDeleteRequest) Expunge(expunge bool) ApiDeleteEventTypeApiV1EventTypeEventTypeNameDeleteRequest {
+	r.expunge = &expunge
+	return r
+}
 func (r ApiDeleteEventTypeApiV1EventTypeEventTypeNameDeleteRequest) IdempotencyKey(idempotencyKey string) ApiDeleteEventTypeApiV1EventTypeEventTypeNameDeleteRequest {
 	r.idempotencyKey = &idempotencyKey
 	return r
@@ -234,6 +239,9 @@ Endpoints already configured to filter on an event type will continue to do so a
 However, new messages can not be sent with it and endpoints can not filter on it.
 An event type can be unarchived with the
 [create operation](#operation/create_event_type_api_v1_event_type__post).
+
+If `expunge=true` is set then the event type is deleted instead of archived.
+This can only be used in development environments.
  * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
  * @param eventTypeName
  * @return ApiDeleteEventTypeApiV1EventTypeEventTypeNameDeleteRequest
@@ -273,6 +281,9 @@ func (a *EventTypeApiService) DeleteEventTypeApiV1EventTypeEventTypeNameDeleteEx
 		return nil, reportError("eventTypeName must have less than 256 elements")
 	}
 
+	if r.expunge != nil {
+		localVarQueryParams.Add("expunge", parameterToString(*r.expunge, ""))
+	}
 	// to determine the Content-Type header
 	localVarHTTPContentTypes := []string{}
 
diff --git a/go/internal/openapi/api_message.go b/go/internal/openapi/api_message.go
index 271a5f83e..bbff05f27 100644
--- a/go/internal/openapi/api_message.go
+++ b/go/internal/openapi/api_message.go
@@ -244,6 +244,186 @@ func (a *MessageApiService) CreateMessageApiV1AppAppIdMsgPostExecute(r ApiCreate
 	return localVarReturnValue, localVarHTTPResponse, nil
 }
 
+type ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest struct {
+	ctx _context.Context
+	ApiService *MessageApiService
+	msgId string
+	appId string
+	idempotencyKey *string
+}
+
+func (r ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest) IdempotencyKey(idempotencyKey string) ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest {
+	r.idempotencyKey = &idempotencyKey
+	return r
+}
+
+func (r ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest) Execute() (*_nethttp.Response, error) {
+	return r.ApiService.ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteExecute(r)
+}
+
+/*
+ * ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDelete Delete message payload
+ * Delete the given message's payload. Useful in cases when a message was accidentally sent with sensitive content.
+
+The message can't be replayed or resent once its payload has been deleted or expired.
+ * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
+ * @param msgId
+ * @param appId
+ * @return ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest
+ */
+func (a *MessageApiService) ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDelete(ctx _context.Context, msgId string, appId string) ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest {
+	return ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest{
+		ApiService: a,
+		ctx: ctx,
+		msgId: msgId,
+		appId: appId,
+	}
+}
+
+/*
+ * Execute executes the request
+ */
+func (a *MessageApiService) ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteExecute(r ApiExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteRequest) (*_nethttp.Response, error) {
+	var (
+		localVarHTTPMethod   = _nethttp.MethodDelete
+		localVarPostBody     interface{}
+		localVarFormFileName string
+		localVarFileName     string
+		localVarFileBytes    []byte
+	)
+
+	localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "MessageApiService.ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDelete")
+	if err != nil {
+		return nil, GenericOpenAPIError{error: err.Error()}
+	}
+
+	localVarPath := localBasePath + "/api/v1/app/{app_id}/msg/{msg_id}/content/"
+	localVarPath = strings.Replace(localVarPath, "{"+"msg_id"+"}", _neturl.PathEscape(parameterToString(r.msgId, "")), -1)
+	localVarPath = strings.Replace(localVarPath, "{"+"app_id"+"}", _neturl.PathEscape(parameterToString(r.appId, "")), -1)
+
+	localVarHeaderParams := make(map[string]string)
+	localVarQueryParams := _neturl.Values{}
+	localVarFormParams := _neturl.Values{}
+	if strlen(r.msgId) < 1 {
+		return nil, reportError("msgId must have at least 1 elements")
+	}
+	if strlen(r.msgId) > 256 {
+		return nil, reportError("msgId must have less than 256 elements")
+	}
+	if strlen(r.appId) < 1 {
+		return nil, reportError("appId must have at least 1 elements")
+	}
+	if strlen(r.appId) > 256 {
+		return nil, reportError("appId must have less than 256 elements")
+	}
+
+	// to determine the Content-Type header
+	localVarHTTPContentTypes := []string{}
+
+	// set Content-Type header
+	localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
+	if localVarHTTPContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHTTPContentType
+	}
+
+	// to determine the Accept header
+	localVarHTTPHeaderAccepts := []string{"application/json"}
+
+	// set Accept header
+	localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
+	if localVarHTTPHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
+	}
+	if r.idempotencyKey != nil {
+		localVarHeaderParams["idempotency-key"] = parameterToString(*r.idempotencyKey, "")
+	}
+	req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	localVarHTTPResponse, err := a.client.callAPI(req)
+	if err != nil || localVarHTTPResponse == nil {
+		return localVarHTTPResponse, err
+	}
+
+	localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body)
+	localVarHTTPResponse.Body.Close()
+	localVarHTTPResponse.Body = _ioutil.NopCloser(bytes.NewBuffer(localVarBody))
+	if err != nil {
+		return localVarHTTPResponse, err
+	}
+
+	if localVarHTTPResponse.StatusCode >= 300 {
+		newErr := GenericOpenAPIError{
+			body:  localVarBody,
+			error: localVarHTTPResponse.Status,
+		}
+		if localVarHTTPResponse.StatusCode == 401 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 403 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 404 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 409 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 422 {
+			var v HTTPValidationError
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 429 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+		}
+		return localVarHTTPResponse, newErr
+	}
+
+	return localVarHTTPResponse, nil
+}
+
 type ApiGetMessageApiV1AppAppIdMsgMsgIdGetRequest struct {
 	ctx _context.Context
 	ApiService *MessageApiService
diff --git a/go/internal/openapi/api_message_attempt.go b/go/internal/openapi/api_message_attempt.go
index b2f826976..77f0f6816 100644
--- a/go/internal/openapi/api_message_attempt.go
+++ b/go/internal/openapi/api_message_attempt.go
@@ -29,6 +29,188 @@ var (
 // MessageAttemptApiService MessageAttemptApi service
 type MessageAttemptApiService service
 
+type ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest struct {
+	ctx _context.Context
+	ApiService *MessageAttemptApiService
+	attemptId string
+	msgId string
+	appId string
+	idempotencyKey *string
+}
+
+func (r ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest) IdempotencyKey(idempotencyKey string) ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest {
+	r.idempotencyKey = &idempotencyKey
+	return r
+}
+
+func (r ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest) Execute() (*_nethttp.Response, error) {
+	return r.ApiService.ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteExecute(r)
+}
+
+/*
+ * ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDelete Delete attempt response body
+ * Deletes the given attempt's repsonse body. Useful when an endpoint accidentally returned sensitive content.
+ * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
+ * @param attemptId
+ * @param msgId
+ * @param appId
+ * @return ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest
+ */
+func (a *MessageAttemptApiService) ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDelete(ctx _context.Context, attemptId string, msgId string, appId string) ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest {
+	return ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest{
+		ApiService: a,
+		ctx: ctx,
+		attemptId: attemptId,
+		msgId: msgId,
+		appId: appId,
+	}
+}
+
+/*
+ * Execute executes the request
+ */
+func (a *MessageAttemptApiService) ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteExecute(r ApiExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteRequest) (*_nethttp.Response, error) {
+	var (
+		localVarHTTPMethod   = _nethttp.MethodDelete
+		localVarPostBody     interface{}
+		localVarFormFileName string
+		localVarFileName     string
+		localVarFileBytes    []byte
+	)
+
+	localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "MessageAttemptApiService.ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDelete")
+	if err != nil {
+		return nil, GenericOpenAPIError{error: err.Error()}
+	}
+
+	localVarPath := localBasePath + "/api/v1/app/{app_id}/msg/{msg_id}/attempt/{attempt_id}/content/"
+	localVarPath = strings.Replace(localVarPath, "{"+"attempt_id"+"}", _neturl.PathEscape(parameterToString(r.attemptId, "")), -1)
+	localVarPath = strings.Replace(localVarPath, "{"+"msg_id"+"}", _neturl.PathEscape(parameterToString(r.msgId, "")), -1)
+	localVarPath = strings.Replace(localVarPath, "{"+"app_id"+"}", _neturl.PathEscape(parameterToString(r.appId, "")), -1)
+
+	localVarHeaderParams := make(map[string]string)
+	localVarQueryParams := _neturl.Values{}
+	localVarFormParams := _neturl.Values{}
+	if strlen(r.msgId) < 1 {
+		return nil, reportError("msgId must have at least 1 elements")
+	}
+	if strlen(r.msgId) > 256 {
+		return nil, reportError("msgId must have less than 256 elements")
+	}
+	if strlen(r.appId) < 1 {
+		return nil, reportError("appId must have at least 1 elements")
+	}
+	if strlen(r.appId) > 256 {
+		return nil, reportError("appId must have less than 256 elements")
+	}
+
+	// to determine the Content-Type header
+	localVarHTTPContentTypes := []string{}
+
+	// set Content-Type header
+	localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
+	if localVarHTTPContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHTTPContentType
+	}
+
+	// to determine the Accept header
+	localVarHTTPHeaderAccepts := []string{"application/json"}
+
+	// set Accept header
+	localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
+	if localVarHTTPHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
+	}
+	if r.idempotencyKey != nil {
+		localVarHeaderParams["idempotency-key"] = parameterToString(*r.idempotencyKey, "")
+	}
+	req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	localVarHTTPResponse, err := a.client.callAPI(req)
+	if err != nil || localVarHTTPResponse == nil {
+		return localVarHTTPResponse, err
+	}
+
+	localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body)
+	localVarHTTPResponse.Body.Close()
+	localVarHTTPResponse.Body = _ioutil.NopCloser(bytes.NewBuffer(localVarBody))
+	if err != nil {
+		return localVarHTTPResponse, err
+	}
+
+	if localVarHTTPResponse.StatusCode >= 300 {
+		newErr := GenericOpenAPIError{
+			body:  localVarBody,
+			error: localVarHTTPResponse.Status,
+		}
+		if localVarHTTPResponse.StatusCode == 401 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 403 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 404 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 409 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 422 {
+			var v HTTPValidationError
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+			return localVarHTTPResponse, newErr
+		}
+		if localVarHTTPResponse.StatusCode == 429 {
+			var v HttpErrorOut
+			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+			if err != nil {
+				newErr.error = err.Error()
+				return localVarHTTPResponse, newErr
+			}
+			newErr.model = v
+		}
+		return localVarHTTPResponse, newErr
+	}
+
+	return localVarHTTPResponse, nil
+}
+
 type ApiGetAttemptApiV1AppAppIdMsgMsgIdAttemptAttemptIdGetRequest struct {
 	ctx _context.Context
 	ApiService *MessageAttemptApiService
diff --git a/go/internal/openapi/model_app_portal_access_in.go b/go/internal/openapi/model_app_portal_access_in.go
index d6a581de8..b44d9975b 100644
--- a/go/internal/openapi/model_app_portal_access_in.go
+++ b/go/internal/openapi/model_app_portal_access_in.go
@@ -16,7 +16,7 @@ import (
 
 // AppPortalAccessIn struct for AppPortalAccessIn
 type AppPortalAccessIn struct {
-	FeatureFlags []string `json:"featureFlags,omitempty"`
+	FeatureFlags *[]string `json:"featureFlags,omitempty"`
 }
 
 // NewAppPortalAccessIn instantiates a new AppPortalAccessIn object
@@ -36,23 +36,22 @@ func NewAppPortalAccessInWithDefaults() *AppPortalAccessIn {
 	return &this
 }
 
-// GetFeatureFlags returns the FeatureFlags field value if set, zero value otherwise (both if not set or set to explicit null).
+// GetFeatureFlags returns the FeatureFlags field value if set, zero value otherwise.
 func (o *AppPortalAccessIn) GetFeatureFlags() []string {
-	if o == nil  {
+	if o == nil || o.FeatureFlags == nil {
 		var ret []string
 		return ret
 	}
-	return o.FeatureFlags
+	return *o.FeatureFlags
 }
 
 // GetFeatureFlagsOk returns a tuple with the FeatureFlags field value if set, nil otherwise
 // and a boolean to check if the value has been set.
-// NOTE: If the value is an explicit nil, `nil, true` will be returned
 func (o *AppPortalAccessIn) GetFeatureFlagsOk() (*[]string, bool) {
 	if o == nil || o.FeatureFlags == nil {
 		return nil, false
 	}
-	return &o.FeatureFlags, true
+	return o.FeatureFlags, true
 }
 
 // HasFeatureFlags returns a boolean if a field has been set.
@@ -66,7 +65,7 @@ func (o *AppPortalAccessIn) HasFeatureFlags() bool {
 
 // SetFeatureFlags gets a reference to the given []string and assigns it to the FeatureFlags field.
 func (o *AppPortalAccessIn) SetFeatureFlags(v []string) {
-	o.FeatureFlags = v
+	o.FeatureFlags = &v
 }
 
 func (o AppPortalAccessIn) MarshalJSON() ([]byte, error) {
diff --git a/go/internal/openapi/model_list_response_application_out_.go b/go/internal/openapi/model_list_response_application_out_.go
index a401e1bfb..1ec8e8d08 100644
--- a/go/internal/openapi/model_list_response_application_out_.go
+++ b/go/internal/openapi/model_list_response_application_out_.go
@@ -19,6 +19,7 @@ type ListResponseApplicationOut struct {
 	Data []ApplicationOut `json:"data"`
 	Done bool `json:"done"`
 	Iterator NullableString `json:"iterator,omitempty"`
+	PrevIterator NullableString `json:"prevIterator,omitempty"`
 }
 
 // NewListResponseApplicationOut instantiates a new ListResponseApplicationOut object
@@ -130,6 +131,48 @@ func (o *ListResponseApplicationOut) UnsetIterator() {
 	o.Iterator.Unset()
 }
 
+// GetPrevIterator returns the PrevIterator field value if set, zero value otherwise (both if not set or set to explicit null).
+func (o *ListResponseApplicationOut) GetPrevIterator() string {
+	if o == nil || o.PrevIterator.Get() == nil {
+		var ret string
+		return ret
+	}
+	return *o.PrevIterator.Get()
+}
+
+// GetPrevIteratorOk returns a tuple with the PrevIterator field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+// NOTE: If the value is an explicit nil, `nil, true` will be returned
+func (o *ListResponseApplicationOut) GetPrevIteratorOk() (*string, bool) {
+	if o == nil  {
+		return nil, false
+	}
+	return o.PrevIterator.Get(), o.PrevIterator.IsSet()
+}
+
+// HasPrevIterator returns a boolean if a field has been set.
+func (o *ListResponseApplicationOut) HasPrevIterator() bool {
+	if o != nil && o.PrevIterator.IsSet() {
+		return true
+	}
+
+	return false
+}
+
+// SetPrevIterator gets a reference to the given NullableString and assigns it to the PrevIterator field.
+func (o *ListResponseApplicationOut) SetPrevIterator(v string) {
+	o.PrevIterator.Set(&v)
+}
+// SetPrevIteratorNil sets the value for PrevIterator to be an explicit nil
+func (o *ListResponseApplicationOut) SetPrevIteratorNil() {
+	o.PrevIterator.Set(nil)
+}
+
+// UnsetPrevIterator ensures that no value is present for PrevIterator, not even an explicit nil
+func (o *ListResponseApplicationOut) UnsetPrevIterator() {
+	o.PrevIterator.Unset()
+}
+
 func (o ListResponseApplicationOut) MarshalJSON() ([]byte, error) {
 	toSerialize := map[string]interface{}{}
 	if true {
@@ -141,6 +184,9 @@ func (o ListResponseApplicationOut) MarshalJSON() ([]byte, error) {
 	if o.Iterator.IsSet() {
 		toSerialize["iterator"] = o.Iterator.Get()
 	}
+	if o.PrevIterator.IsSet() {
+		toSerialize["prevIterator"] = o.PrevIterator.Get()
+	}
 	return json.Marshal(toSerialize)
 }
 
diff --git a/go/internal/openapi/model_list_response_endpoint_out_.go b/go/internal/openapi/model_list_response_endpoint_out_.go
index b0d48fd76..fd7eaafbc 100644
--- a/go/internal/openapi/model_list_response_endpoint_out_.go
+++ b/go/internal/openapi/model_list_response_endpoint_out_.go
@@ -19,6 +19,7 @@ type ListResponseEndpointOut struct {
 	Data []EndpointOut `json:"data"`
 	Done bool `json:"done"`
 	Iterator NullableString `json:"iterator,omitempty"`
+	PrevIterator NullableString `json:"prevIterator,omitempty"`
 }
 
 // NewListResponseEndpointOut instantiates a new ListResponseEndpointOut object
@@ -130,6 +131,48 @@ func (o *ListResponseEndpointOut) UnsetIterator() {
 	o.Iterator.Unset()
 }
 
+// GetPrevIterator returns the PrevIterator field value if set, zero value otherwise (both if not set or set to explicit null).
+func (o *ListResponseEndpointOut) GetPrevIterator() string {
+	if o == nil || o.PrevIterator.Get() == nil {
+		var ret string
+		return ret
+	}
+	return *o.PrevIterator.Get()
+}
+
+// GetPrevIteratorOk returns a tuple with the PrevIterator field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+// NOTE: If the value is an explicit nil, `nil, true` will be returned
+func (o *ListResponseEndpointOut) GetPrevIteratorOk() (*string, bool) {
+	if o == nil  {
+		return nil, false
+	}
+	return o.PrevIterator.Get(), o.PrevIterator.IsSet()
+}
+
+// HasPrevIterator returns a boolean if a field has been set.
+func (o *ListResponseEndpointOut) HasPrevIterator() bool {
+	if o != nil && o.PrevIterator.IsSet() {
+		return true
+	}
+
+	return false
+}
+
+// SetPrevIterator gets a reference to the given NullableString and assigns it to the PrevIterator field.
+func (o *ListResponseEndpointOut) SetPrevIterator(v string) {
+	o.PrevIterator.Set(&v)
+}
+// SetPrevIteratorNil sets the value for PrevIterator to be an explicit nil
+func (o *ListResponseEndpointOut) SetPrevIteratorNil() {
+	o.PrevIterator.Set(nil)
+}
+
+// UnsetPrevIterator ensures that no value is present for PrevIterator, not even an explicit nil
+func (o *ListResponseEndpointOut) UnsetPrevIterator() {
+	o.PrevIterator.Unset()
+}
+
 func (o ListResponseEndpointOut) MarshalJSON() ([]byte, error) {
 	toSerialize := map[string]interface{}{}
 	if true {
@@ -141,6 +184,9 @@ func (o ListResponseEndpointOut) MarshalJSON() ([]byte, error) {
 	if o.Iterator.IsSet() {
 		toSerialize["iterator"] = o.Iterator.Get()
 	}
+	if o.PrevIterator.IsSet() {
+		toSerialize["prevIterator"] = o.PrevIterator.Get()
+	}
 	return json.Marshal(toSerialize)
 }
 
diff --git a/go/internal/openapi/model_list_response_event_type_out_.go b/go/internal/openapi/model_list_response_event_type_out_.go
index 9f5bde000..0e1eec62b 100644
--- a/go/internal/openapi/model_list_response_event_type_out_.go
+++ b/go/internal/openapi/model_list_response_event_type_out_.go
@@ -19,6 +19,7 @@ type ListResponseEventTypeOut struct {
 	Data []EventTypeOut `json:"data"`
 	Done bool `json:"done"`
 	Iterator NullableString `json:"iterator,omitempty"`
+	PrevIterator NullableString `json:"prevIterator,omitempty"`
 }
 
 // NewListResponseEventTypeOut instantiates a new ListResponseEventTypeOut object
@@ -130,6 +131,48 @@ func (o *ListResponseEventTypeOut) UnsetIterator() {
 	o.Iterator.Unset()
 }
 
+// GetPrevIterator returns the PrevIterator field value if set, zero value otherwise (both if not set or set to explicit null).
+func (o *ListResponseEventTypeOut) GetPrevIterator() string {
+	if o == nil || o.PrevIterator.Get() == nil {
+		var ret string
+		return ret
+	}
+	return *o.PrevIterator.Get()
+}
+
+// GetPrevIteratorOk returns a tuple with the PrevIterator field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+// NOTE: If the value is an explicit nil, `nil, true` will be returned
+func (o *ListResponseEventTypeOut) GetPrevIteratorOk() (*string, bool) {
+	if o == nil  {
+		return nil, false
+	}
+	return o.PrevIterator.Get(), o.PrevIterator.IsSet()
+}
+
+// HasPrevIterator returns a boolean if a field has been set.
+func (o *ListResponseEventTypeOut) HasPrevIterator() bool {
+	if o != nil && o.PrevIterator.IsSet() {
+		return true
+	}
+
+	return false
+}
+
+// SetPrevIterator gets a reference to the given NullableString and assigns it to the PrevIterator field.
+func (o *ListResponseEventTypeOut) SetPrevIterator(v string) {
+	o.PrevIterator.Set(&v)
+}
+// SetPrevIteratorNil sets the value for PrevIterator to be an explicit nil
+func (o *ListResponseEventTypeOut) SetPrevIteratorNil() {
+	o.PrevIterator.Set(nil)
+}
+
+// UnsetPrevIterator ensures that no value is present for PrevIterator, not even an explicit nil
+func (o *ListResponseEventTypeOut) UnsetPrevIterator() {
+	o.PrevIterator.Unset()
+}
+
 func (o ListResponseEventTypeOut) MarshalJSON() ([]byte, error) {
 	toSerialize := map[string]interface{}{}
 	if true {
@@ -141,6 +184,9 @@ func (o ListResponseEventTypeOut) MarshalJSON() ([]byte, error) {
 	if o.Iterator.IsSet() {
 		toSerialize["iterator"] = o.Iterator.Get()
 	}
+	if o.PrevIterator.IsSet() {
+		toSerialize["prevIterator"] = o.PrevIterator.Get()
+	}
 	return json.Marshal(toSerialize)
 }
 
diff --git a/go/internal/openapi/model_message_in.go b/go/internal/openapi/model_message_in.go
index 9d7729216..5106fd93c 100644
--- a/go/internal/openapi/model_message_in.go
+++ b/go/internal/openapi/model_message_in.go
@@ -16,6 +16,7 @@ import (
 
 // MessageIn struct for MessageIn
 type MessageIn struct {
+	Application *ApplicationIn `json:"application,omitempty"`
 	// List of free-form identifiers that endpoints can filter by
 	Channels []string `json:"channels,omitempty"`
 	// Optional unique identifier for the message
@@ -49,6 +50,38 @@ func NewMessageInWithDefaults() *MessageIn {
 	return &this
 }
 
+// GetApplication returns the Application field value if set, zero value otherwise.
+func (o *MessageIn) GetApplication() ApplicationIn {
+	if o == nil || o.Application == nil {
+		var ret ApplicationIn
+		return ret
+	}
+	return *o.Application
+}
+
+// GetApplicationOk returns a tuple with the Application field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *MessageIn) GetApplicationOk() (*ApplicationIn, bool) {
+	if o == nil || o.Application == nil {
+		return nil, false
+	}
+	return o.Application, true
+}
+
+// HasApplication returns a boolean if a field has been set.
+func (o *MessageIn) HasApplication() bool {
+	if o != nil && o.Application != nil {
+		return true
+	}
+
+	return false
+}
+
+// SetApplication gets a reference to the given ApplicationIn and assigns it to the Application field.
+func (o *MessageIn) SetApplication(v ApplicationIn) {
+	o.Application = &v
+}
+
 // GetChannels returns the Channels field value if set, zero value otherwise (both if not set or set to explicit null).
 func (o *MessageIn) GetChannels() []string {
 	if o == nil  {
@@ -206,6 +239,9 @@ func (o *MessageIn) SetPayloadRetentionPeriod(v int32) {
 
 func (o MessageIn) MarshalJSON() ([]byte, error) {
 	toSerialize := map[string]interface{}{}
+	if o.Application != nil {
+		toSerialize["application"] = o.Application
+	}
 	if o.Channels != nil {
 		toSerialize["channels"] = o.Channels
 	}
diff --git a/go/internal/openapi/model_ordering.go b/go/internal/openapi/model_ordering.go
new file mode 100644
index 000000000..5cd1cbe0d
--- /dev/null
+++ b/go/internal/openapi/model_ordering.go
@@ -0,0 +1,110 @@
+/*
+ * Svix API
+ *
+ * Welcome to the Svix API documentation!  Useful links: [Homepage](https://www.svix.com) | [Support email](mailto:support+docs@svix.com) | [Blog](https://www.svix.com/blog/) | [Slack Community](https://www.svix.com/slack/)  # Introduction  This is the reference documentation and schemas for the [Svix webhook service](https://www.svix.com) API. For tutorials and other documentation please refer to [the documentation](https://docs.svix.com).  ## Main concepts  In Svix you have four important entities you will be interacting with:  - `messages`: these are the webhooks being sent. They can have contents and a few other properties. - `application`: this is where `messages` are sent to. Usually you want to create one application for each user on your platform. - `endpoint`: endpoints are the URLs messages will be sent to. Each application can have multiple `endpoints` and each message sent to that application will be sent to all of them (unless they are not subscribed to the sent event type). - `event-type`: event types are identifiers denoting the type of the message being sent. Event types are primarily used to decide which events are sent to which endpoint.   ## Authentication  Get your authentication token (`AUTH_TOKEN`) from the [Svix dashboard](https://dashboard.svix.com) and use it as part of the `Authorization` header as such: `Authorization: Bearer ${AUTH_TOKEN}`.  <SecurityDefinitions />   ## Code samples  The code samples assume you already have the respective libraries installed and you know how to use them. For the latest information on how to do that, please refer to [the documentation](https://docs.svix.com/).   ## Idempotency  Svix supports [idempotency](https://en.wikipedia.org/wiki/Idempotence) for safely retrying requests without accidentally performing the same operation twice. This is useful when an API call is disrupted in transit and you do not receive a response.  To perform an idempotent request, pass the idempotency key in the `Idempotency-Key` header to the request. The idempotency key should be a unique value generated by the client. You can create the key in however way you like, though we suggest using UUID v4, or any other string with enough entropy to avoid collisions.  Svix's idempotency works by saving the resulting status code and body of the first request made for any given idempotency key for any successful request. Subsequent requests with the same key return the same result.  Please note that idempotency is only supported for `POST` requests.   ## Cross-Origin Resource Sharing  This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/). And that allows cross-domain communication from the browser. All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site. 
+ *
+ * API version: 1.4
+ */
+
+// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
+
+package openapi
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+// Ordering Defines the ordering in a listing of results.
+type Ordering string
+
+// List of Ordering
+const (
+	ORDERING_ASCENDING Ordering = "ascending"
+	ORDERING_DESCENDING Ordering = "descending"
+)
+
+var allowedOrderingEnumValues = []Ordering{
+	"ascending",
+	"descending",
+}
+
+func (v *Ordering) UnmarshalJSON(src []byte) error {
+	var value string
+	err := json.Unmarshal(src, &value)
+	if err != nil {
+		return err
+	}
+	enumTypeValue := Ordering(value)
+	for _, existing := range allowedOrderingEnumValues {
+		if existing == enumTypeValue {
+			*v = enumTypeValue
+			return nil
+		}
+	}
+
+	return fmt.Errorf("%+v is not a valid Ordering", value)
+}
+
+// NewOrderingFromValue returns a pointer to a valid Ordering
+// for the value passed as argument, or an error if the value passed is not allowed by the enum
+func NewOrderingFromValue(v string) (*Ordering, error) {
+	ev := Ordering(v)
+	if ev.IsValid() {
+		return &ev, nil
+	} else {
+		return nil, fmt.Errorf("invalid value '%v' for Ordering: valid values are %v", v, allowedOrderingEnumValues)
+	}
+}
+
+// IsValid return true if the value is valid for the enum, false otherwise
+func (v Ordering) IsValid() bool {
+	for _, existing := range allowedOrderingEnumValues {
+		if existing == v {
+			return true
+		}
+	}
+	return false
+}
+
+// Ptr returns reference to Ordering value
+func (v Ordering) Ptr() *Ordering {
+	return &v
+}
+
+type NullableOrdering struct {
+	value *Ordering
+	isSet bool
+}
+
+func (v NullableOrdering) Get() *Ordering {
+	return v.value
+}
+
+func (v *NullableOrdering) Set(val *Ordering) {
+	v.value = val
+	v.isSet = true
+}
+
+func (v NullableOrdering) IsSet() bool {
+	return v.isSet
+}
+
+func (v *NullableOrdering) Unset() {
+	v.value = nil
+	v.isSet = false
+}
+
+func NewNullableOrdering(val *Ordering) *NullableOrdering {
+	return &NullableOrdering{value: val, isSet: true}
+}
+
+func (v NullableOrdering) MarshalJSON() ([]byte, error) {
+	return json.Marshal(v.value)
+}
+
+func (v *NullableOrdering) UnmarshalJSON(src []byte) error {
+	v.isSet = true
+	return json.Unmarshal(src, &v.value)
+}
+
diff --git a/go/internal/version/version.go b/go/internal/version/version.go
index 4c0334e2f..06f1ddf5b 100644
--- a/go/internal/version/version.go
+++ b/go/internal/version/version.go
@@ -1,3 +1,3 @@
 package version
 
-const Version = "0.75.0"
+const Version = "0.81.0"
diff --git a/go/message.go b/go/message.go
index 7c52aae42..d41807ffc 100644
--- a/go/message.go
+++ b/go/message.go
@@ -26,8 +26,8 @@ type MessageListOptions struct {
 	Channel    *string
 }
 
-func (m *Message) List(appId string, options *MessageListOptions) (*ListResponseMessageOut, error) {
-	req := m.api.MessageApi.ListMessagesApiV1AppAppIdMsgGet(context.Background(), appId)
+func (m *Message) List(ctx context.Context, appId string, options *MessageListOptions) (*ListResponseMessageOut, error) {
+	req := m.api.MessageApi.ListMessagesApiV1AppAppIdMsgGet(ctx, appId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -56,12 +56,12 @@ func (m *Message) List(appId string, options *MessageListOptions) (*ListResponse
 	return &ret, nil
 }
 
-func (m *Message) Create(appId string, messageIn *MessageIn) (*MessageOut, error) {
-	return m.CreateWithOptions(appId, messageIn, nil)
+func (m *Message) Create(ctx context.Context, appId string, messageIn *MessageIn) (*MessageOut, error) {
+	return m.CreateWithOptions(ctx, appId, messageIn, nil)
 }
 
-func (m *Message) CreateWithOptions(appId string, messageIn *MessageIn, options *PostOptions) (*MessageOut, error) {
-	req := m.api.MessageApi.CreateMessageApiV1AppAppIdMsgPost(context.Background(), appId)
+func (m *Message) CreateWithOptions(ctx context.Context, appId string, messageIn *MessageIn, options *PostOptions) (*MessageOut, error) {
+	req := m.api.MessageApi.CreateMessageApiV1AppAppIdMsgPost(ctx, appId)
 	req = req.MessageIn(openapi.MessageIn(*messageIn))
 	if options != nil {
 		if options.IdempotencyKey != nil {
@@ -76,8 +76,8 @@ func (m *Message) CreateWithOptions(appId string, messageIn *MessageIn, options
 	return &ret, nil
 }
 
-func (m *Message) Get(appId string, msgId string) (*MessageOut, error) {
-	req := m.api.MessageApi.GetMessageApiV1AppAppIdMsgMsgIdGet(context.Background(), msgId, appId)
+func (m *Message) Get(ctx context.Context, appId string, msgId string) (*MessageOut, error) {
+	req := m.api.MessageApi.GetMessageApiV1AppAppIdMsgMsgIdGet(ctx, msgId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -85,3 +85,9 @@ func (m *Message) Get(appId string, msgId string) (*MessageOut, error) {
 	ret := MessageOut(out)
 	return &ret, nil
 }
+
+func (m *Message) ExpungeContent(ctx context.Context, appId string, msgId string) error {
+	req := m.api.MessageApi.ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDelete(ctx, msgId, appId)
+	res, err := req.Execute()
+	return wrapError(err, res)
+}
diff --git a/go/messageattempt.go b/go/messageattempt.go
index 6f8c2c9d2..750b9a1ec 100644
--- a/go/messageattempt.go
+++ b/go/messageattempt.go
@@ -34,12 +34,12 @@ type MessageAttemptListOptions struct {
 }
 
 // Deprecated: use `ListByMsg` or `ListByEndpoint` instead
-func (m *MessageAttempt) List(appId string, msgId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptOut, error) {
-	return m.ListByMsg(appId, msgId, options)
+func (m *MessageAttempt) List(ctx context.Context, appId string, msgId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptOut, error) {
+	return m.ListByMsg(ctx, appId, msgId, options)
 }
 
-func (m *MessageAttempt) ListByMsg(appId string, msgId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptOut, error) {
-	req := m.api.MessageAttemptApi.ListAttemptsByMsgApiV1AppAppIdAttemptMsgMsgIdGet(context.Background(), appId, msgId)
+func (m *MessageAttempt) ListByMsg(ctx context.Context, appId string, msgId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptOut, error) {
+	req := m.api.MessageAttemptApi.ListAttemptsByMsgApiV1AppAppIdAttemptMsgMsgIdGet(ctx, appId, msgId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -74,8 +74,8 @@ func (m *MessageAttempt) ListByMsg(appId string, msgId string, options *MessageA
 	return &ret, nil
 }
 
-func (m *MessageAttempt) ListByEndpoint(appId string, endpointId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptOut, error) {
-	req := m.api.MessageAttemptApi.ListAttemptsByEndpointApiV1AppAppIdAttemptEndpointEndpointIdGet(context.Background(), appId, endpointId)
+func (m *MessageAttempt) ListByEndpoint(ctx context.Context, appId string, endpointId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptOut, error) {
+	req := m.api.MessageAttemptApi.ListAttemptsByEndpointApiV1AppAppIdAttemptEndpointEndpointIdGet(ctx, appId, endpointId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -110,8 +110,8 @@ func (m *MessageAttempt) ListByEndpoint(appId string, endpointId string, options
 	return &ret, nil
 }
 
-func (m *MessageAttempt) Get(appId string, msgId string, attemptID string) (*MessageAttemptOut, error) {
-	req := m.api.MessageAttemptApi.GetAttemptApiV1AppAppIdMsgMsgIdAttemptAttemptIdGet(context.Background(), attemptID, msgId, appId)
+func (m *MessageAttempt) Get(ctx context.Context, appId string, msgId string, attemptID string) (*MessageAttemptOut, error) {
+	req := m.api.MessageAttemptApi.GetAttemptApiV1AppAppIdMsgMsgIdAttemptAttemptIdGet(ctx, attemptID, msgId, appId)
 	out, res, err := req.Execute()
 	if err != nil {
 		return nil, wrapError(err, res)
@@ -120,12 +120,12 @@ func (m *MessageAttempt) Get(appId string, msgId string, attemptID string) (*Mes
 	return &ret, nil
 }
 
-func (m *MessageAttempt) Resend(appId string, msgId string, endpointId string) error {
-	return m.ResendWithOptions(appId, msgId, endpointId, nil)
+func (m *MessageAttempt) Resend(ctx context.Context, appId string, msgId string, endpointId string) error {
+	return m.ResendWithOptions(ctx, appId, msgId, endpointId, nil)
 }
 
-func (m *MessageAttempt) ResendWithOptions(appId string, msgId string, endpointId string, options *PostOptions) error {
-	req := m.api.MessageAttemptApi.ResendWebhookApiV1AppAppIdMsgMsgIdEndpointEndpointIdResendPost(context.Background(), endpointId, msgId, appId)
+func (m *MessageAttempt) ResendWithOptions(ctx context.Context, appId string, msgId string, endpointId string, options *PostOptions) error {
+	req := m.api.MessageAttemptApi.ResendWebhookApiV1AppAppIdMsgMsgIdEndpointEndpointIdResendPost(ctx, endpointId, msgId, appId)
 	if options != nil {
 		if options.IdempotencyKey != nil {
 			req = req.IdempotencyKey(*options.IdempotencyKey)
@@ -135,8 +135,8 @@ func (m *MessageAttempt) ResendWithOptions(appId string, msgId string, endpointI
 	return wrapError(err, res)
 }
 
-func (m *MessageAttempt) ListAttemptedMessages(appId string, endpointId string, options *MessageAttemptListOptions) (*ListResponseEndpointMessageOut, error) {
-	req := m.api.MessageAttemptApi.ListAttemptedMessagesApiV1AppAppIdEndpointEndpointIdMsgGet(context.Background(), endpointId, appId)
+func (m *MessageAttempt) ListAttemptedMessages(ctx context.Context, appId string, endpointId string, options *MessageAttemptListOptions) (*ListResponseEndpointMessageOut, error) {
+	req := m.api.MessageAttemptApi.ListAttemptedMessagesApiV1AppAppIdEndpointEndpointIdMsgGet(ctx, endpointId, appId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -165,8 +165,8 @@ func (m *MessageAttempt) ListAttemptedMessages(appId string, endpointId string,
 	return &ret, nil
 }
 
-func (m *MessageAttempt) ListAttemptedDestinations(appId string, msgId string, options *MessageAttemptListOptions) (*ListResponseMessageEndpointOut, error) {
-	req := m.api.MessageAttemptApi.ListAttemptedDestinationsApiV1AppAppIdMsgMsgIdEndpointGet(context.Background(), msgId, appId)
+func (m *MessageAttempt) ListAttemptedDestinations(ctx context.Context, appId string, msgId string, options *MessageAttemptListOptions) (*ListResponseMessageEndpointOut, error) {
+	req := m.api.MessageAttemptApi.ListAttemptedDestinationsApiV1AppAppIdMsgMsgIdEndpointGet(ctx, msgId, appId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -183,8 +183,8 @@ func (m *MessageAttempt) ListAttemptedDestinations(appId string, msgId string, o
 	return &ret, nil
 }
 
-func (m *MessageAttempt) ListAttemptsForEndpoint(appId string, msgId string, endpointId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptEndpointOut, error) {
-	req := m.api.MessageAttemptApi.ListAttemptsForEndpointApiV1AppAppIdMsgMsgIdEndpointEndpointIdAttemptGet(context.Background(), msgId, appId, endpointId)
+func (m *MessageAttempt) ListAttemptsForEndpoint(ctx context.Context, appId string, msgId string, endpointId string, options *MessageAttemptListOptions) (*ListResponseMessageAttemptEndpointOut, error) {
+	req := m.api.MessageAttemptApi.ListAttemptsForEndpointApiV1AppAppIdMsgMsgIdEndpointEndpointIdAttemptGet(ctx, msgId, appId, endpointId)
 	if options != nil {
 		if options.Iterator != nil {
 			req = req.Iterator(*options.Iterator)
@@ -215,3 +215,9 @@ func (m *MessageAttempt) ListAttemptsForEndpoint(appId string, msgId string, end
 	ret := ListResponseMessageAttemptEndpointOut(out)
 	return &ret, nil
 }
+
+func (m *MessageAttempt) ExpungeContent(ctx context.Context, appId string, msgId string, attemptId string) error {
+	req := m.api.MessageAttemptApi.ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDelete(ctx, attemptId, msgId, appId)
+	res, err := req.Execute()
+	return wrapError(err, res)
+}
diff --git a/go/webhook.go b/go/webhook.go
index 471050f98..087ff2497 100644
--- a/go/webhook.go
+++ b/go/webhook.go
@@ -40,7 +40,7 @@ func NewWebhook(secret string) (*Webhook, error) {
 }
 
 func NewWebhookRaw(secret []byte) (*Webhook, error) {
-	return &Webhook {
+	return &Webhook{
 		key: secret,
 	}, nil
 }
diff --git a/java/README.md b/java/README.md
index c72a57451..007c64aa7 100644
--- a/java/README.md
+++ b/java/README.md
@@ -51,7 +51,7 @@ Add this dependency to your project's POM:
 <dependency>
   <groupId>com.svix</groupId>
   <artifactId>svix</artifactId>
-  <version>0.75.0</version>
+  <version>0.81.0</version>
   <scope>compile</scope>
 </dependency>
 ```
@@ -61,7 +61,7 @@ Add this dependency to your project's POM:
 Add this dependency to your project's build file:
 
 ```groovy
-implementation "com.svix:svix:0.75.0"
+implementation "com.svix:svix:0.81.0"
 ```
 
 # Development
diff --git a/java/gradle.properties b/java/gradle.properties
index 84394a7b7..63cf72285 100644
--- a/java/gradle.properties
+++ b/java/gradle.properties
@@ -1,5 +1,5 @@
 GROUP=com.svix
-VERSION_NAME=0.75.0
+VERSION_NAME=0.81.0
 
 POM_URL=https://github.com/svix/svix-webhooks
 POM_SCM_URL=git@github.com:svix/svix-webhooks.git
diff --git a/java/lib/src/main/java/com/svix/Application.java b/java/lib/src/main/java/com/svix/Application.java
index 9c8141db2..806a19562 100644
--- a/java/lib/src/main/java/com/svix/Application.java
+++ b/java/lib/src/main/java/com/svix/Application.java
@@ -15,7 +15,7 @@ public final class Application {
 
 	public ListResponseApplicationOut list(final ApplicationListOptions options) throws ApiException {
 		try {
-			return api.listApplicationsApiV1AppGet(options.getIterator(), options.getLimit(), null);
+			return api.listApplicationsApiV1AppGet(options.getIterator(), options.getLimit(), options.getOrder(), null);
 		} catch (com.svix.internal.ApiException e) {
 			throw Utils.wrapInternalApiException(e);
 		}
diff --git a/java/lib/src/main/java/com/svix/ApplicationListOptions.java b/java/lib/src/main/java/com/svix/ApplicationListOptions.java
index 593b208db..70aa70cde 100644
--- a/java/lib/src/main/java/com/svix/ApplicationListOptions.java
+++ b/java/lib/src/main/java/com/svix/ApplicationListOptions.java
@@ -1,4 +1,15 @@
 package com.svix;
 
+import com.svix.models.Ordering;
+
 public class ApplicationListOptions extends ListOptions {
+    private Ordering order;
+
+    public void setOrder(final Ordering order) {
+        this.order = order;
+    }
+
+    public Ordering getOrder() {
+        return this.order;
+    }
 }
diff --git a/java/lib/src/main/java/com/svix/Endpoint.java b/java/lib/src/main/java/com/svix/Endpoint.java
index 6d38a236e..08671eb25 100644
--- a/java/lib/src/main/java/com/svix/Endpoint.java
+++ b/java/lib/src/main/java/com/svix/Endpoint.java
@@ -10,8 +10,11 @@
 import com.svix.models.EndpointUpdate;
 import com.svix.models.EndpointSecretOut;
 import com.svix.models.EndpointSecretRotateIn;
+import com.svix.models.EndpointTransformationIn;
+import com.svix.models.EndpointTransformationOut;
 import com.svix.models.ListResponseEndpointOut;
 import com.svix.models.RecoverIn;
+import com.svix.models.ReplayIn;
 import com.svix.models.EndpointStats;
 
 public final class Endpoint {
@@ -23,7 +26,7 @@ public Endpoint() {
 
 	public ListResponseEndpointOut list(final String appId, final EndpointListOptions options) throws ApiException {
 		try {
-			return api.listEndpointsApiV1AppAppIdEndpointGet(appId, options.getIterator(), options.getLimit(), null);
+			return api.listEndpointsApiV1AppAppIdEndpointGet(appId, options.getIterator(), options.getLimit(), options.getOrder(), null);
 		} catch (com.svix.internal.ApiException e) {
 			throw Utils.wrapInternalApiException(e);
 		}
@@ -128,4 +131,32 @@ public EndpointStats getStats(final String appId, final String endpointId) throw
 			throw Utils.wrapInternalApiException(e);
 		}
 	}
+
+	public void replayMissing(final String appId, final String endpointId, final ReplayIn replayIn) throws ApiException {
+		this.replayMissing(appId, endpointId, replayIn, new PostOptions());
+	}
+
+	public void replayMissing(final String appId, final String endpointId, final ReplayIn replayIn, final PostOptions options) throws ApiException {
+		try {
+			api.replayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostWithHttpInfo(appId, endpointId, replayIn, options.getIdempotencyKey());
+		} catch (com.svix.internal.ApiException e) {
+			throw Utils.wrapInternalApiException(e);
+		}
+	}
+
+	public EndpointTransformationOut transformationGet(final String appId, final String endpointId) throws ApiException {
+		try {
+			return api.getEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationGet(endpointId, appId, null);
+		} catch (com.svix.internal.ApiException e) {
+			throw Utils.wrapInternalApiException(e);
+		}
+	}
+
+	public void transformationPartialUpdate(final String appId, final String endpointId, final EndpointTransformationIn transformationIn) throws ApiException {
+		try {
+			api.setEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationPatch(appId, endpointId, transformationIn, null);
+		} catch (com.svix.internal.ApiException e) {
+			throw Utils.wrapInternalApiException(e);
+		}
+	}
 }
diff --git a/java/lib/src/main/java/com/svix/EndpointListOptions.java b/java/lib/src/main/java/com/svix/EndpointListOptions.java
index d300af8a6..8019cc79c 100644
--- a/java/lib/src/main/java/com/svix/EndpointListOptions.java
+++ b/java/lib/src/main/java/com/svix/EndpointListOptions.java
@@ -1,4 +1,15 @@
 package com.svix;
 
+import com.svix.models.Ordering;
+
 public class EndpointListOptions extends ListOptions {
+    private Ordering order;
+
+    public void setOrder(final Ordering order) {
+        this.order = order;
+    }
+
+    public Ordering getOrder() {
+        return this.order;
+    }
 }
diff --git a/java/lib/src/main/java/com/svix/EventType.java b/java/lib/src/main/java/com/svix/EventType.java
index ca6ddb245..b02a118b8 100644
--- a/java/lib/src/main/java/com/svix/EventType.java
+++ b/java/lib/src/main/java/com/svix/EventType.java
@@ -52,7 +52,7 @@ public EventTypeOut update(final String eventTypeName, final EventTypeUpdate eve
 
 	public void delete(final String eventTypeName) throws ApiException {
 		try {
-			api.deleteEventTypeApiV1EventTypeEventTypeNameDelete(eventTypeName, null);
+			api.deleteEventTypeApiV1EventTypeEventTypeNameDelete(eventTypeName, null, null);
 		} catch (com.svix.internal.ApiException e) {
 			throw Utils.wrapInternalApiException(e);
 		}
diff --git a/java/lib/src/main/java/com/svix/Message.java b/java/lib/src/main/java/com/svix/Message.java
index dcaede0ba..133a5c054 100644
--- a/java/lib/src/main/java/com/svix/Message.java
+++ b/java/lib/src/main/java/com/svix/Message.java
@@ -51,6 +51,14 @@ public MessageOut get(final String msgId, final String appId) throws ApiExceptio
 		}
 	}
 
+	public void expungeContent(final String msgId, final String appId) throws ApiException {
+		try {
+			api.expungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDelete(msgId, appId, null);
+		} catch (com.svix.internal.ApiException e) {
+			throw Utils.wrapInternalApiException(e);
+		}
+	}
+
 	private static Object getPayload(final Object payload) {
 		// Convert string to JsonObject, otherwise gson fails to convert it.
 		if (payload instanceof String) {
diff --git a/java/lib/src/main/java/com/svix/MessageAttempt.java b/java/lib/src/main/java/com/svix/MessageAttempt.java
index 9bf76cf69..44ea07b97 100644
--- a/java/lib/src/main/java/com/svix/MessageAttempt.java
+++ b/java/lib/src/main/java/com/svix/MessageAttempt.java
@@ -130,4 +130,17 @@ public ListResponseMessageAttemptEndpointOut listAttemptsForEndpoint(final Strin
 				throw Utils.wrapInternalApiException(e);
 			}
 	}
+
+	public void expungeContent(final String appId, final String msgId, final String attemptId) throws ApiException {
+		try {
+			api.expungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDelete(
+				attemptId,
+				msgId,
+				appId,
+				null
+			);
+		} catch (com.svix.internal.ApiException e) {
+			throw Utils.wrapInternalApiException(e);
+		}
+	}
 }
diff --git a/java/lib/src/main/java/com/svix/Svix.java b/java/lib/src/main/java/com/svix/Svix.java
index db85df906..822dbf79c 100644
--- a/java/lib/src/main/java/com/svix/Svix.java
+++ b/java/lib/src/main/java/com/svix/Svix.java
@@ -5,7 +5,7 @@
 import com.svix.internal.auth.HttpBearerAuth;
 
 public final class Svix {
-	public static final String VERSION = "0.75.0";
+	public static final String VERSION = "0.81.0";
 	private final Application application;
 	private final Authentication authentication;
 	private final Endpoint endpoint;
diff --git a/javascript/package.json b/javascript/package.json
index 7badb77a1..339adcbe2 100644
--- a/javascript/package.json
+++ b/javascript/package.json
@@ -1,6 +1,6 @@
 {
   "name": "svix",
-  "version": "0.75.0",
+  "version": "0.81.0",
   "description": "Svix API client",
   "author": "svix",
   "repository": "https://github.com/svix/svix-libs",
diff --git a/javascript/src/index.ts b/javascript/src/index.ts
index 31c1fc226..f1b223d9f 100644
--- a/javascript/src/index.ts
+++ b/javascript/src/index.ts
@@ -14,6 +14,8 @@ import {
   EndpointUpdate,
   EndpointSecretOut,
   EndpointSecretRotateIn,
+  EndpointTransformationIn,
+  EndpointTransformationOut,
   EndpointHeadersIn,
   EndpointHeadersPatchIn,
   EndpointHeadersOut,
@@ -50,6 +52,7 @@ import {
   ResponseContext,
   AppPortalAccessOut,
   AppPortalAccessIn,
+  Ordering,
 } from "./openapi/index";
 export * from "./openapi/models/all";
 export * from "./openapi/apis/exception";
@@ -58,7 +61,7 @@ import * as base64 from "@stablelib/base64";
 import * as sha256 from "fast-sha256";
 
 const WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes
-const VERSION = "0.75.0";
+const VERSION = "0.81.0";
 
 class UserAgentMiddleware implements Middleware {
   public pre(context: RequestContext): Promise<RequestContext> {
@@ -163,9 +166,13 @@ interface ListOptions {
   limit?: number;
 }
 
-export type ApplicationListOptions = ListOptions;
+export interface ApplicationListOptions extends ListOptions {
+  order?: Ordering;
+}
 
-export type EndpointListOptions = ListOptions;
+export interface EndpointListOptions extends ListOptions {
+  order?: Ordering;
+}
 
 export type IntegrationListOptions = ListOptions;
 
@@ -318,7 +325,7 @@ class Endpoint {
       .then(() => Promise.resolve());
   }
 
-  public replay(
+  public replayMissing(
     appId: string,
     endpointId: string,
     replayIn: ReplayIn,
@@ -371,6 +378,25 @@ class Endpoint {
       endpointId,
     });
   }
+
+  public transformationGet(
+    appId: string,
+    endpointId: string
+  ): Promise<EndpointTransformationOut> {
+    return this.api.getEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationGet(
+      { endpointId, appId }
+    );
+  }
+
+  public transformationPartialUpdate(
+    appId: string,
+    endpointId: string,
+    endpointTransformationIn: EndpointTransformationIn
+  ): Promise<void> {
+    return this.api.setEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationPatch(
+      { appId, endpointId, endpointTransformationIn }
+    );
+  }
 }
 
 class EventType {
@@ -501,6 +527,10 @@ class Message {
   public get(appId: string, msgId: string): Promise<MessageOut> {
     return this.api.getMessageApiV1AppAppIdMsgMsgIdGet({ msgId, appId });
   }
+
+  public expungeContent(appId: string, msgId: string): Promise<void> {
+    return this.api.expungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDelete({ appId, msgId });
+  }
 }
 
 class MessageAttempt {
@@ -605,6 +635,16 @@ class MessageAttempt {
       { appId, msgId, endpointId, ...options }
     );
   }
+
+  public expungeContent(
+    appId: string,
+    msgId: string,
+    attemptId: string
+  ): Promise<void> {
+    return this.api.expungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDelete({
+      appId, msgId, attemptId
+    })
+  }
 }
 
 class ExtendableError extends Error {
@@ -714,7 +754,8 @@ export class Webhook {
 
   public sign(msgId: string, timestamp: Date, payload: string): string {
     const encoder = new TextEncoder();
-    const toSign = encoder.encode(`${msgId}.${timestamp.getTime() / 1000}.${payload}`);
+    const timestampNumber = Math.floor(timestamp.getTime() / 1000);
+    const toSign = encoder.encode(`${msgId}.${timestampNumber}.${payload}`);
     const expectedSignature = base64.encode(sha256.hmac(this.key, toSign));
     return `v1,${expectedSignature}`;
   }
diff --git a/kotlin/README.md b/kotlin/README.md
index b6cfb928a..b5757b1c4 100644
--- a/kotlin/README.md
+++ b/kotlin/README.md
@@ -51,7 +51,7 @@ Add this dependency to your project's POM:
 <dependency>
   <groupId>com.svix.kotlin</groupId>
   <artifactId>svix-kotlin</artifactId>
-  <version>0.75.0</version>
+  <version>0.81.0</version>
   <scope>compile</scope>
 </dependency>
 ```
@@ -61,7 +61,7 @@ Add this dependency to your project's POM:
 Add this dependency to your project's build file:
 
 ```groovy
-implementation "com.svix.kotlin:svix-kotlin:0.75.0"
+implementation "com.svix.kotlin:svix-kotlin:0.81.0"
 ```
 
 # Development
diff --git a/kotlin/gradle.properties b/kotlin/gradle.properties
index 36968becb..8c95fe8c0 100644
--- a/kotlin/gradle.properties
+++ b/kotlin/gradle.properties
@@ -1,5 +1,5 @@
 GROUP=com.svix.kotlin
-VERSION_NAME=0.75.0
+VERSION_NAME=0.81.0
 
 POM_URL=https://github.com/svix/svix-webhooks
 POM_SCM_URL=git@github.com:svix/svix-webhooks.git
diff --git a/kotlin/lib/src/main/kotlin/Application.kt b/kotlin/lib/src/main/kotlin/Application.kt
index 51fdf40fa..cc9dc7393 100644
--- a/kotlin/lib/src/main/kotlin/Application.kt
+++ b/kotlin/lib/src/main/kotlin/Application.kt
@@ -18,7 +18,7 @@ class Application internal constructor(token: String, options: SvixOptions) {
 
     suspend fun list(options: ApplicationListOptions = ApplicationListOptions()): ListResponseApplicationOut {
         try {
-            return api.listApplicationsApiV1AppGet(options.iterator, options.limit, null)
+            return api.listApplicationsApiV1AppGet(options.iterator, options.limit, options.order, null)
         } catch (e: Exception) {
             throw ApiException.wrap(e)
         }
diff --git a/kotlin/lib/src/main/kotlin/ApplicationListOptions.kt b/kotlin/lib/src/main/kotlin/ApplicationListOptions.kt
index 3111bc746..68e9b501f 100644
--- a/kotlin/lib/src/main/kotlin/ApplicationListOptions.kt
+++ b/kotlin/lib/src/main/kotlin/ApplicationListOptions.kt
@@ -1,6 +1,12 @@
 package com.svix.kotlin
 
+import com.svix.kotlin.models.Ordering
+
 class ApplicationListOptions : ListOptions() {
+    var order: Ordering? = null
+
+    fun order(order: Ordering) = apply { this.order = order }
+
     override fun iterator(iterator: String): ApplicationListOptions = apply { super.iterator(iterator) }
 
     override fun limit(limit: Int) = apply { super.limit(limit) }
diff --git a/kotlin/lib/src/main/kotlin/Endpoint.kt b/kotlin/lib/src/main/kotlin/Endpoint.kt
index 73e01d335..a8adcd9e1 100644
--- a/kotlin/lib/src/main/kotlin/Endpoint.kt
+++ b/kotlin/lib/src/main/kotlin/Endpoint.kt
@@ -10,9 +10,12 @@ import com.svix.kotlin.models.EndpointOut
 import com.svix.kotlin.models.EndpointSecretOut
 import com.svix.kotlin.models.EndpointSecretRotateIn
 import com.svix.kotlin.models.EndpointStats
+import com.svix.kotlin.models.EndpointTransformationIn
+import com.svix.kotlin.models.EndpointTransformationOut
 import com.svix.kotlin.models.EndpointUpdate
 import com.svix.kotlin.models.ListResponseEndpointOut
 import com.svix.kotlin.models.RecoverIn
+import com.svix.kotlin.models.ReplayIn
 
 class Endpoint internal constructor(token: String, options: SvixOptions) {
     val api = EndpointApi(options.serverUrl)
@@ -33,6 +36,7 @@ class Endpoint internal constructor(token: String, options: SvixOptions) {
                 appId,
                 options.iterator,
                 options.limit,
+                options.order,
                 null
             )
         } catch (e: Exception) {
@@ -194,4 +198,47 @@ class Endpoint internal constructor(token: String, options: SvixOptions) {
             throw ApiException.wrap(e)
         }
     }
+
+    suspend fun replayMissing(
+        appId: String,
+        endpointId: String,
+        replayIn: ReplayIn,
+        options: PostOptions = PostOptions()
+    ) {
+        try {
+            api.replayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPost(
+                appId,
+                endpointId,
+                replayIn,
+                options.idempotencyKey
+            )
+        } catch (e: Exception) {
+            throw ApiException.wrap(e)
+        }
+    }
+
+    suspend fun transformationGet(appId: String, endpointId: String): EndpointTransformationOut {
+        try {
+            return api.getEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationGet(
+                endpointId,
+                appId,
+                null
+            )
+        } catch (e: Exception) {
+            throw ApiException.wrap(e)
+        }
+    }
+
+    suspend fun transformationPartialUpdate(appId: String, endpointId: String, endpointTransformationIn: EndpointTransformationIn) {
+        try {
+            api.setEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationPatch(
+                endpointId,
+                appId,
+                endpointTransformationIn,
+                null
+            )
+        } catch (e: Exception) {
+            throw ApiException.wrap(e)
+        }
+    }
 }
diff --git a/kotlin/lib/src/main/kotlin/EndpointListOptions.kt b/kotlin/lib/src/main/kotlin/EndpointListOptions.kt
index c937b2c19..ed727806e 100644
--- a/kotlin/lib/src/main/kotlin/EndpointListOptions.kt
+++ b/kotlin/lib/src/main/kotlin/EndpointListOptions.kt
@@ -1,6 +1,12 @@
 package com.svix.kotlin
 
+import com.svix.kotlin.models.Ordering
+
 class EndpointListOptions : ListOptions() {
+    var order: Ordering? = null
+
+    fun order(order: Ordering) = apply { this.order = order }
+
     override fun iterator(iterator: String) = apply { super.iterator(iterator) }
 
     override fun limit(limit: Int) = apply { super.limit(limit) }
diff --git a/kotlin/lib/src/main/kotlin/EventType.kt b/kotlin/lib/src/main/kotlin/EventType.kt
index 17fd471bb..c31840167 100644
--- a/kotlin/lib/src/main/kotlin/EventType.kt
+++ b/kotlin/lib/src/main/kotlin/EventType.kt
@@ -51,7 +51,7 @@ class EventType internal constructor(token: String, options: SvixOptions) {
 
     suspend fun delete(eventTypeName: String) {
         try {
-            api.deleteEventTypeApiV1EventTypeEventTypeNameDelete(eventTypeName, null)
+            api.deleteEventTypeApiV1EventTypeEventTypeNameDelete(eventTypeName, null, null)
         } catch (e: Exception) {
             throw ApiException.wrap(e)
         }
diff --git a/kotlin/lib/src/main/kotlin/Message.kt b/kotlin/lib/src/main/kotlin/Message.kt
index 07ea7259d..a58de51b6 100644
--- a/kotlin/lib/src/main/kotlin/Message.kt
+++ b/kotlin/lib/src/main/kotlin/Message.kt
@@ -48,4 +48,12 @@ class Message internal constructor(token: String, options: SvixOptions) {
             throw ApiException.wrap(e)
         }
     }
+
+    suspend fun expungeContent(msgId: String, appId: String) {
+        try {
+            return api.expungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDelete(msgId, appId, null)
+        } catch (e: Exception) {
+            throw ApiException.wrap(e)
+        }
+    }
 }
diff --git a/kotlin/lib/src/main/kotlin/MessageAttempt.kt b/kotlin/lib/src/main/kotlin/MessageAttempt.kt
index 5eed444b4..9ecfb0801 100644
--- a/kotlin/lib/src/main/kotlin/MessageAttempt.kt
+++ b/kotlin/lib/src/main/kotlin/MessageAttempt.kt
@@ -152,4 +152,12 @@ class MessageAttempt internal constructor(token: String, options: SvixOptions) {
             throw ApiException.wrap(e)
         }
     }
+
+    suspend fun expungeContent(appId: String, msgId: String, attemptId: String) {
+        try {
+            return api.expungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDelete(attemptId, msgId, appId, null)
+        } catch (e: Exception) {
+            throw ApiException.wrap(e)
+        }
+    }
 }
diff --git a/kotlin/lib/src/main/kotlin/SvixOptions.kt b/kotlin/lib/src/main/kotlin/SvixOptions.kt
index 7cb3edd63..72a0120e8 100644
--- a/kotlin/lib/src/main/kotlin/SvixOptions.kt
+++ b/kotlin/lib/src/main/kotlin/SvixOptions.kt
@@ -6,7 +6,7 @@ data class SvixOptions(
     val numRetries: Int? = null
 ) {
 
-    private val version = "0.75.0"
+    private val version = "0.81.0"
 
     var serverUrl: String
         get() = this.wantedServerUrl ?: DEFAULT_URL
diff --git a/openapi.json b/openapi.json
index 2c0a09598..98c15780e 100644
--- a/openapi.json
+++ b/openapi.json
@@ -4,13 +4,14 @@
             "AppPortalAccessIn": {
                 "properties": {
                     "featureFlags": {
+                        "default": [],
+                        "example": [],
                         "items": {
                             "example": "cool-new-feature",
                             "maxLength": 256,
                             "pattern": "^[a-zA-Z0-9\\-_.]+$",
                             "type": "string"
                         },
-                        "nullable": true,
                         "title": "Featureflags",
                         "type": "array",
                         "uniqueItems": true
@@ -1839,6 +1840,12 @@
                         "nullable": true,
                         "title": "Iterator",
                         "type": "string"
+                    },
+                    "prevIterator": {
+                        "example": "-iterator",
+                        "nullable": true,
+                        "title": "Previterator",
+                        "type": "string"
                     }
                 },
                 "required": [
@@ -1926,6 +1933,12 @@
                         "nullable": true,
                         "title": "Iterator",
                         "type": "string"
+                    },
+                    "prevIterator": {
+                        "example": "-iterator",
+                        "nullable": true,
+                        "title": "Previterator",
+                        "type": "string"
                     }
                 },
                 "required": [
@@ -1953,6 +1966,12 @@
                         "nullable": true,
                         "title": "Iterator",
                         "type": "string"
+                    },
+                    "prevIterator": {
+                        "example": "-iterator",
+                        "nullable": true,
+                        "title": "Previterator",
+                        "type": "string"
                     }
                 },
                 "required": [
@@ -2586,6 +2605,10 @@
             },
             "MessageIn": {
                 "properties": {
+                    "application": {
+                        "$ref": "#/components/schemas/ApplicationIn",
+                        "nullable": true
+                    },
                     "channels": {
                         "description": "List of free-form identifiers that endpoints can filter by",
                         "example": [
@@ -2730,6 +2753,15 @@
                     "Sending"
                 ]
             },
+            "Ordering": {
+                "description": "Defines the ordering in a listing of results.",
+                "enum": [
+                    "ascending",
+                    "descending"
+                ],
+                "title": "Ordering",
+                "type": "string"
+            },
             "RecoverIn": {
                 "properties": {
                     "since": {
@@ -3092,6 +3124,19 @@
                             "type": "integer"
                         }
                     },
+                    {
+                        "in": "query",
+                        "name": "order",
+                        "required": false,
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/components/schemas/Ordering"
+                                }
+                            ],
+                            "default": "descending"
+                        }
+                    },
                     {
                         "description": "The request's idempotency key",
                         "in": "header",
@@ -4753,6 +4798,19 @@
                             "type": "integer"
                         }
                     },
+                    {
+                        "in": "query",
+                        "name": "order",
+                        "required": false,
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/components/schemas/Ordering"
+                                }
+                            ],
+                            "default": "descending"
+                        }
+                    },
                     {
                         "description": "The request's idempotency key",
                         "in": "header",
@@ -7730,57 +7788,57 @@
                     {
                         "label": "JavaScript",
                         "lang": "JavaScript",
-                        "source": "const endpointTransformationOut = await svix.endpoint.get('app_id', 'endpoint_id');"
+                        "source": "const endpointTransformationOut = await svix.endpoint.transformationGet('app_id', 'endpoint_id');"
                     },
                     {
                         "label": "TypeScript",
                         "lang": "JavaScript",
-                        "source": "const endpointTransformationOut = await svix.endpoint.get('app_id', 'endpoint_id');"
+                        "source": "const endpointTransformationOut = await svix.endpoint.transformationGet('app_id', 'endpoint_id');"
                     },
                     {
                         "label": "Python",
                         "lang": "Python",
-                        "source": "endpoint_transformation_out = svix.endpoint.get('app_id', 'endpoint_id')"
+                        "source": "endpoint_transformation_out = svix.endpoint.transformation_get('app_id', 'endpoint_id')"
                     },
                     {
                         "label": "Python (Async)",
                         "lang": "Python",
-                        "source": "endpoint_transformation_out = await svix.endpoint.get('app_id', 'endpoint_id')"
+                        "source": "endpoint_transformation_out = await svix.endpoint.transformation_get('app_id', 'endpoint_id')"
                     },
                     {
                         "label": "Go",
                         "lang": "Go",
-                        "source": "endpointTransformationOut, err := svixClient.Endpoint.Get(\"app_id\", \"endpoint_id\")"
+                        "source": "endpointTransformationOut, err := svixClient.Endpoint.TransformationGet(\"app_id\", \"endpoint_id\")"
                     },
                     {
                         "label": "Kotlin",
                         "lang": "Kotlin",
-                        "source": "val endpointTransformationOut = svix.endpoint.get('app_id', 'endpoint_id')"
+                        "source": "val endpointTransformationOut = svix.endpoint.transformationGet('app_id', 'endpoint_id')"
                     },
                     {
                         "label": "Java",
                         "lang": "Java",
-                        "source": "EndpointTransformationOut endpointTransformationOut = svix.getEndpoint().get('app_id', 'endpoint_id')"
+                        "source": "EndpointTransformationOut endpointTransformationOut = svix.getEndpoint().transformationGet('app_id', 'endpoint_id')"
                     },
                     {
                         "label": "Ruby",
                         "lang": "Ruby",
-                        "source": "endpoint_transformation_out = svix.endpoint.get('app_id', 'endpoint_id')"
+                        "source": "endpoint_transformation_out = svix.endpoint.transformation_get('app_id', 'endpoint_id')"
                     },
                     {
                         "label": "Rust",
                         "lang": "Rust",
-                        "source": "let endpoint_transformation_out = svix.endpoint().get(\"app_id\", \"endpoint_id\").await?;"
+                        "source": "let endpoint_transformation_out = svix.endpoint().transformation_get(\"app_id\", \"endpoint_id\").await?;"
                     },
                     {
                         "label": "C#",
                         "lang": "C#",
-                        "source": "var endpointTransformationOut = await svix.Endpoint.GetAsync(\"app_id\", \"endpoint_id\")"
+                        "source": "var endpointTransformationOut = await svix.Endpoint.TransformationGetAsync(\"app_id\", \"endpoint_id\")"
                     },
                     {
                         "label": "CLI",
                         "lang": "Shell",
-                        "source": "svix endpoint get 'app_id' 'endpoint_id'"
+                        "source": "svix endpoint transformation-get 'app_id' 'endpoint_id'"
                     },
                     {
                         "label": "cURL",
@@ -7922,57 +7980,57 @@
                     {
                         "label": "JavaScript",
                         "lang": "JavaScript",
-                        "source": "await svix.endpoint.set('app_id', 'endpoint_id', {\n    code: None,\n    enabled: None\n});"
+                        "source": "await svix.endpoint.transformationPartialUpdate('app_id', 'endpoint_id', {\n    code: None,\n    enabled: None\n});"
                     },
                     {
                         "label": "TypeScript",
                         "lang": "JavaScript",
-                        "source": "await svix.endpoint.set('app_id', 'endpoint_id', {\n    code: None,\n    enabled: None\n});"
+                        "source": "await svix.endpoint.transformationPartialUpdate('app_id', 'endpoint_id', {\n    code: None,\n    enabled: None\n});"
                     },
                     {
                         "label": "Python",
                         "lang": "Python",
-                        "source": "svix.endpoint.set('app_id', 'endpoint_id', EndpointTransformationIn(\n    code=None,\n    enabled=None\n))"
+                        "source": "svix.endpoint.transformation_partial_update('app_id', 'endpoint_id', EndpointTransformationIn(\n    code=None,\n    enabled=None\n))"
                     },
                     {
                         "label": "Python (Async)",
                         "lang": "Python",
-                        "source": "await svix.endpoint.set('app_id', 'endpoint_id', EndpointTransformationIn(\n    code=None,\n    enabled=None\n))"
+                        "source": "await svix.endpoint.transformation_partial_update('app_id', 'endpoint_id', EndpointTransformationIn(\n    code=None,\n    enabled=None\n))"
                     },
                     {
                         "label": "Go",
                         "lang": "Go",
-                        "source": "err := svixClient.Endpoint.Set(\"app_id\", \"endpoint_id\", &svix.EndpointTransformationIn{\n    Code: None,\n    Enabled: None\n})"
+                        "source": "err := svixClient.Endpoint.TransformationPartialUpdate(\"app_id\", \"endpoint_id\", &svix.EndpointTransformationIn{\n    Code: None,\n    Enabled: None\n})"
                     },
                     {
                         "label": "Kotlin",
                         "lang": "Kotlin",
-                        "source": "svix.endpoint.set('app_id', 'endpoint_id', EndpointTransformationIn()\n    .code(None),\n    .enabled(None)\n)"
+                        "source": "svix.endpoint.transformationPartialUpdate('app_id', 'endpoint_id', EndpointTransformationIn()\n    .code(None),\n    .enabled(None)\n)"
                     },
                     {
                         "label": "Java",
                         "lang": "Java",
-                        "source": "svix.getEndpoint().set('app_id', 'endpoint_id', new EndpointTransformationIn()\n    .code(None),\n    .enabled(None)\n)"
+                        "source": "svix.getEndpoint().transformationPartialUpdate('app_id', 'endpoint_id', new EndpointTransformationIn()\n    .code(None),\n    .enabled(None)\n)"
                     },
                     {
                         "label": "Ruby",
                         "lang": "Ruby",
-                        "source": "svix.endpoint.set('app_id', 'endpoint_id', Svix::EndpointTransformationIn.new({\n    \"code\": null,\n    \"enabled\": null\n}))"
+                        "source": "svix.endpoint.transformation_partial_update('app_id', 'endpoint_id', Svix::EndpointTransformationIn.new({\n    \"code\": null,\n    \"enabled\": null\n}))"
                     },
                     {
                         "label": "Rust",
                         "lang": "Rust",
-                        "source": "svix.endpoint().set(\"app_id\", \"endpoint_id\", EndpointTransformationIn {\n    code: None,\n    enabled: None\n}).await?;"
+                        "source": "svix.endpoint().transformation_partial_update(\"app_id\", \"endpoint_id\", EndpointTransformationIn {\n    code: None,\n    enabled: None\n}).await?;"
                     },
                     {
                         "label": "C#",
                         "lang": "C#",
-                        "source": "await svix.Endpoint.SetAsync(\"app_id\", \"endpoint_id\", new EndpointTransformationIn{\n    code: None,\n    enabled: None\n})"
+                        "source": "await svix.Endpoint.TransformationPartialUpdateAsync(\"app_id\", \"endpoint_id\", new EndpointTransformationIn{\n    code: None,\n    enabled: None\n})"
                     },
                     {
                         "label": "CLI",
                         "lang": "Shell",
-                        "source": "svix endpoint set 'app_id' 'endpoint_id' '{\n    \"code\": null,\n    \"enabled\": null\n}'"
+                        "source": "svix endpoint transformation-partial-update 'app_id' 'endpoint_id' '{\n    \"code\": null,\n    \"enabled\": null\n}'"
                     },
                     {
                         "label": "cURL",
@@ -9905,62 +9963,62 @@
                     {
                         "label": "JavaScript",
                         "lang": "JavaScript",
-                        "source": "const messageOut = await svix.message.create('app_id', {\n    eventType: \"user.signup\",\n    eventId: \"evt_pNZKtWg8Azow\",\n    channels: [\n        \"project_123\",\n        \"group_2\"\n    ],\n    payload: {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    payloadRetentionPeriod: 90\n});"
+                        "source": "const messageOut = await svix.message.create('app_id', {\n    eventType: \"user.signup\",\n    eventId: \"evt_pNZKtWg8Azow\",\n    channels: [\n        \"project_123\",\n        \"group_2\"\n    ],\n    payload: {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    payloadRetentionPeriod: 90,\n    application: None\n});"
                     },
                     {
                         "label": "TypeScript",
                         "lang": "JavaScript",
-                        "source": "const messageOut = await svix.message.create('app_id', {\n    eventType: \"user.signup\",\n    eventId: \"evt_pNZKtWg8Azow\",\n    channels: [\n        \"project_123\",\n        \"group_2\"\n    ],\n    payload: {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    payloadRetentionPeriod: 90\n});"
+                        "source": "const messageOut = await svix.message.create('app_id', {\n    eventType: \"user.signup\",\n    eventId: \"evt_pNZKtWg8Azow\",\n    channels: [\n        \"project_123\",\n        \"group_2\"\n    ],\n    payload: {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    payloadRetentionPeriod: 90,\n    application: None\n});"
                     },
                     {
                         "label": "Python",
                         "lang": "Python",
-                        "source": "message_out = svix.message.create('app_id', MessageIn(\n    event_type=\"user.signup\",\n    event_id=\"evt_pNZKtWg8Azow\",\n    channels=['project_123', 'group_2'],\n    payload={'username': 'test_user', 'email': 'test@example.com'},\n    payload_retention_period=90\n))"
+                        "source": "message_out = svix.message.create('app_id', MessageIn(\n    event_type=\"user.signup\",\n    event_id=\"evt_pNZKtWg8Azow\",\n    channels=['project_123', 'group_2'],\n    payload={'username': 'test_user', 'email': 'test@example.com'},\n    payload_retention_period=90,\n    application=None\n))"
                     },
                     {
                         "label": "Python (Async)",
                         "lang": "Python",
-                        "source": "message_out = await svix.message.create('app_id', MessageIn(\n    event_type=\"user.signup\",\n    event_id=\"evt_pNZKtWg8Azow\",\n    channels=['project_123', 'group_2'],\n    payload={'username': 'test_user', 'email': 'test@example.com'},\n    payload_retention_period=90\n))"
+                        "source": "message_out = await svix.message.create('app_id', MessageIn(\n    event_type=\"user.signup\",\n    event_id=\"evt_pNZKtWg8Azow\",\n    channels=['project_123', 'group_2'],\n    payload={'username': 'test_user', 'email': 'test@example.com'},\n    payload_retention_period=90,\n    application=None\n))"
                     },
                     {
                         "label": "Go",
                         "lang": "Go",
-                        "source": "messageOut, err := svixClient.Message.Create(\"app_id\", &svix.MessageIn{\n    EventType: \"user.signup\",\n    EventId: \"evt_pNZKtWg8Azow\",\n    Channels: [...]string{\"project_123\", \"group_2\"},\n    Payload: map[string]interface{}{\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    PayloadRetentionPeriod: 90\n})"
+                        "source": "messageOut, err := svixClient.Message.Create(\"app_id\", &svix.MessageIn{\n    EventType: \"user.signup\",\n    EventId: \"evt_pNZKtWg8Azow\",\n    Channels: [...]string{\"project_123\", \"group_2\"},\n    Payload: map[string]interface{}{\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    PayloadRetentionPeriod: 90,\n    Application: None\n})"
                     },
                     {
                         "label": "Kotlin",
                         "lang": "Kotlin",
-                        "source": "val messageOut = svix.message.create('app_id', MessageIn()\n    .eventType(\"user.signup\"),\n    .eventId(\"evt_pNZKtWg8Azow\"),\n    .channels(arrayOf(\"project_123\", \"group_2\")),\n    .payload(\"\"\"{\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    }\"\"\"),\n    .payloadRetentionPeriod(90)\n)"
+                        "source": "val messageOut = svix.message.create('app_id', MessageIn()\n    .eventType(\"user.signup\"),\n    .eventId(\"evt_pNZKtWg8Azow\"),\n    .channels(arrayOf(\"project_123\", \"group_2\")),\n    .payload(\"\"\"{\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    }\"\"\"),\n    .payloadRetentionPeriod(90),\n    .application(None)\n)"
                     },
                     {
                         "label": "Java",
                         "lang": "Java",
-                        "source": "MessageOut messageOut = svix.getMessage().create('app_id', new MessageIn()\n    .eventType(\"user.signup\"),\n    .eventId(\"evt_pNZKtWg8Azow\"),\n    .channels(new String[]{\"project_123\", \"group_2\"}),\n    .payload(\"{\" +\n        \"\\\"username\\\": \\\"test_user\\\",\" +\n        \"\\\"email\\\": \\\"test@example.com\\\"\" +\n    \"}\"),\n    .payloadRetentionPeriod(90)\n)"
+                        "source": "MessageOut messageOut = svix.getMessage().create('app_id', new MessageIn()\n    .eventType(\"user.signup\"),\n    .eventId(\"evt_pNZKtWg8Azow\"),\n    .channels(new String[]{\"project_123\", \"group_2\"}),\n    .payload(\"{\" +\n        \"\\\"username\\\": \\\"test_user\\\",\" +\n        \"\\\"email\\\": \\\"test@example.com\\\"\" +\n    \"}\"),\n    .payloadRetentionPeriod(90),\n    .application(None)\n)"
                     },
                     {
                         "label": "Ruby",
                         "lang": "Ruby",
-                        "source": "message_out = svix.message.create('app_id', Svix::MessageIn.new({\n    \"event_type\": \"user.signup\",\n    \"event_id\": \"evt_pNZKtWg8Azow\",\n    \"channels\": [\n        \"project_123\",\n        \"group_2\"\n    ],\n    \"payload\": {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    \"payload_retention_period\": 90\n}))"
+                        "source": "message_out = svix.message.create('app_id', Svix::MessageIn.new({\n    \"event_type\": \"user.signup\",\n    \"event_id\": \"evt_pNZKtWg8Azow\",\n    \"channels\": [\n        \"project_123\",\n        \"group_2\"\n    ],\n    \"payload\": {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    \"payload_retention_period\": 90,\n    \"application\": null\n}))"
                     },
                     {
                         "label": "Rust",
                         "lang": "Rust",
-                        "source": "let message_out = svix.message().create(\"app_id\", MessageIn {\n    event_type: \"user.signup\".to_string(),\n    event_id: Some(\"evt_pNZKtWg8Azow\".to_string()),\n    channels: Some(vec![\"project_123\".to_string(), \"group_2\".to_string()]),\n    payload: json!({\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    }),\n    payload_retention_period: Some(90)\n}, None).await?;"
+                        "source": "let message_out = svix.message().create(\"app_id\", MessageIn {\n    event_type: \"user.signup\".to_string(),\n    event_id: Some(\"evt_pNZKtWg8Azow\".to_string()),\n    channels: Some(vec![\"project_123\".to_string(), \"group_2\".to_string()]),\n    payload: json!({\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    }),\n    payload_retention_period: Some(90),\n    application: None\n}, None).await?;"
                     },
                     {
                         "label": "C#",
                         "lang": "C#",
-                        "source": "var messageOut = await svix.Message.CreateAsync(\"app_id\", new MessageIn{\n    eventType: \"user.signup\",\n    eventId: \"evt_pNZKtWg8Azow\",\n    channels: new string[] {\"project_123\", \"group_2\"},\n    payload: new {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    payloadRetentionPeriod: 90\n})"
+                        "source": "var messageOut = await svix.Message.CreateAsync(\"app_id\", new MessageIn{\n    eventType: \"user.signup\",\n    eventId: \"evt_pNZKtWg8Azow\",\n    channels: new string[] {\"project_123\", \"group_2\"},\n    payload: new {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    payloadRetentionPeriod: 90,\n    application: None\n})"
                     },
                     {
                         "label": "CLI",
                         "lang": "Shell",
-                        "source": "svix message create 'app_id' '{\n    \"eventType\": \"user.signup\",\n    \"eventId\": \"evt_pNZKtWg8Azow\",\n    \"channels\": [\n        \"project_123\",\n        \"group_2\"\n    ],\n    \"payload\": {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    \"payloadRetentionPeriod\": 90\n}'"
+                        "source": "svix message create 'app_id' '{\n    \"eventType\": \"user.signup\",\n    \"eventId\": \"evt_pNZKtWg8Azow\",\n    \"channels\": [\n        \"project_123\",\n        \"group_2\"\n    ],\n    \"payload\": {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    \"payloadRetentionPeriod\": 90,\n    \"application\": null\n}'"
                     },
                     {
                         "label": "cURL",
                         "lang": "Shell",
-                        "source": "curl -X 'POST' \\\n  'https://api.svix.com/api/v1/app/{app_id}/msg/' \\\n  -H 'Authorization: Bearer AUTH_TOKEN' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"eventType\": \"user.signup\",\n    \"eventId\": \"evt_pNZKtWg8Azow\",\n    \"channels\": [\n        \"project_123\",\n        \"group_2\"\n    ],\n    \"payload\": {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    \"payloadRetentionPeriod\": 90\n}'"
+                        "source": "curl -X 'POST' \\\n  'https://api.svix.com/api/v1/app/{app_id}/msg/' \\\n  -H 'Authorization: Bearer AUTH_TOKEN' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"eventType\": \"user.signup\",\n    \"eventId\": \"evt_pNZKtWg8Azow\",\n    \"channels\": [\n        \"project_123\",\n        \"group_2\"\n    ],\n    \"payload\": {\n        \"username\": \"test_user\",\n        \"email\": \"test@example.com\"\n    },\n    \"payloadRetentionPeriod\": 90,\n    \"application\": null\n}'"
                     }
                 ]
             }
@@ -10646,6 +10704,200 @@
                 ]
             }
         },
+        "/api/v1/app/{app_id}/msg/{msg_id}/attempt/{attempt_id}/content/": {
+            "delete": {
+                "description": "Deletes the given attempt's repsonse body. Useful when an endpoint accidentally returned sensitive content.",
+                "operationId": "expunge_attempt_content_api_v1_app__app_id__msg__msg_id__attempt__attempt_id__content__delete",
+                "parameters": [
+                    {
+                        "in": "path",
+                        "name": "attempt_id",
+                        "required": true,
+                        "schema": {
+                            "example": "atmpt_1srOrx2ZWZBpBUvZwXKQmoEYga2",
+                            "title": "Attempt Id",
+                            "type": "string"
+                        }
+                    },
+                    {
+                        "in": "path",
+                        "name": "msg_id",
+                        "required": true,
+                        "schema": {
+                            "description": "The message's ID or eventID",
+                            "example": "msg_1srOrx2ZWZBpBUvZwXKQmoEYga2",
+                            "maxLength": 256,
+                            "minLength": 1,
+                            "pattern": "^[a-zA-Z0-9\\-_.]+$",
+                            "title": "Msg Id",
+                            "type": "string"
+                        }
+                    },
+                    {
+                        "in": "path",
+                        "name": "app_id",
+                        "required": true,
+                        "schema": {
+                            "description": "The application's ID or UID",
+                            "example": "app_1srOrx2ZWZBpBUvZwXKQmoEYga2",
+                            "maxLength": 256,
+                            "minLength": 1,
+                            "pattern": "^[a-zA-Z0-9\\-_.]+$",
+                            "title": "App Id",
+                            "type": "string"
+                        }
+                    },
+                    {
+                        "description": "The request's idempotency key",
+                        "in": "header",
+                        "name": "idempotency-key",
+                        "required": false,
+                        "schema": {
+                            "description": "The request's idempotency key",
+                            "nullable": true,
+                            "title": "Idempotency-Key",
+                            "type": "string"
+                        }
+                    }
+                ],
+                "responses": {
+                    "204": {
+                        "description": "Successful Response"
+                    },
+                    "401": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Unauthorized"
+                    },
+                    "403": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Forbidden"
+                    },
+                    "404": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Not Found"
+                    },
+                    "409": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Conflict"
+                    },
+                    "422": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                        "description": "Validation Error"
+                    },
+                    "429": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Too Many Requests"
+                    }
+                },
+                "security": [
+                    {
+                        "HTTPBearer": []
+                    }
+                ],
+                "summary": "Delete attempt response body",
+                "tags": [
+                    "Message Attempt"
+                ],
+                "x-codeSamples": [
+                    {
+                        "label": "JavaScript",
+                        "lang": "JavaScript",
+                        "source": "await svix.messageAttempt.expungeContent('app_id', 'msg_id', 'attempt_id');"
+                    },
+                    {
+                        "label": "TypeScript",
+                        "lang": "JavaScript",
+                        "source": "await svix.messageAttempt.expungeContent('app_id', 'msg_id', 'attempt_id');"
+                    },
+                    {
+                        "label": "Python",
+                        "lang": "Python",
+                        "source": "svix.message_attempt.expunge_content('app_id', 'msg_id', 'attempt_id')"
+                    },
+                    {
+                        "label": "Python (Async)",
+                        "lang": "Python",
+                        "source": "await svix.message_attempt.expunge_content('app_id', 'msg_id', 'attempt_id')"
+                    },
+                    {
+                        "label": "Go",
+                        "lang": "Go",
+                        "source": "err := svixClient.MessageAttempt.ExpungeContent(\"app_id\", \"msg_id\", \"attempt_id\")"
+                    },
+                    {
+                        "label": "Kotlin",
+                        "lang": "Kotlin",
+                        "source": "svix.messageAttempt.expungeContent('app_id', 'msg_id', 'attempt_id')"
+                    },
+                    {
+                        "label": "Java",
+                        "lang": "Java",
+                        "source": "svix.getMessageAttempt().expungeContent('app_id', 'msg_id', 'attempt_id')"
+                    },
+                    {
+                        "label": "Ruby",
+                        "lang": "Ruby",
+                        "source": "svix.message_attempt.expunge_content('app_id', 'msg_id', 'attempt_id')"
+                    },
+                    {
+                        "label": "Rust",
+                        "lang": "Rust",
+                        "source": "svix.message_attempt().expunge_content(\"app_id\", \"msg_id\", \"attempt_id\").await?;"
+                    },
+                    {
+                        "label": "C#",
+                        "lang": "C#",
+                        "source": "await svix.MessageAttempt.ExpungeContentAsync(\"app_id\", \"msg_id\", \"attempt_id\")"
+                    },
+                    {
+                        "label": "CLI",
+                        "lang": "Shell",
+                        "source": "svix message-attempt expunge-content 'app_id' 'msg_id' 'attempt_id'"
+                    },
+                    {
+                        "label": "cURL",
+                        "lang": "Shell",
+                        "source": "curl -X 'DELETE' \\\n  'https://api.svix.com/api/v1/app/{app_id}/msg/{msg_id}/attempt/{attempt_id}/content/' \\\n  -H 'Authorization: Bearer AUTH_TOKEN' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json'"
+                    }
+                ]
+            }
+        },
         "/api/v1/app/{app_id}/msg/{msg_id}/attempt/{attempt_id}/headers/": {
             "get": {
                 "description": "Calculate and return headers used on a given message attempt",
@@ -10847,6 +11099,190 @@
                 ]
             }
         },
+        "/api/v1/app/{app_id}/msg/{msg_id}/content/": {
+            "delete": {
+                "description": "Delete the given message's payload. Useful in cases when a message was accidentally sent with sensitive content.\n\nThe message can't be replayed or resent once its payload has been deleted or expired.",
+                "operationId": "expunge_message_payload_api_v1_app__app_id__msg__msg_id__content__delete",
+                "parameters": [
+                    {
+                        "in": "path",
+                        "name": "msg_id",
+                        "required": true,
+                        "schema": {
+                            "description": "The message's ID or eventID",
+                            "example": "msg_1srOrx2ZWZBpBUvZwXKQmoEYga2",
+                            "maxLength": 256,
+                            "minLength": 1,
+                            "pattern": "^[a-zA-Z0-9\\-_.]+$",
+                            "title": "Msg Id",
+                            "type": "string"
+                        }
+                    },
+                    {
+                        "in": "path",
+                        "name": "app_id",
+                        "required": true,
+                        "schema": {
+                            "description": "The application's ID or UID",
+                            "example": "app_1srOrx2ZWZBpBUvZwXKQmoEYga2",
+                            "maxLength": 256,
+                            "minLength": 1,
+                            "pattern": "^[a-zA-Z0-9\\-_.]+$",
+                            "title": "App Id",
+                            "type": "string"
+                        }
+                    },
+                    {
+                        "description": "The request's idempotency key",
+                        "in": "header",
+                        "name": "idempotency-key",
+                        "required": false,
+                        "schema": {
+                            "description": "The request's idempotency key",
+                            "nullable": true,
+                            "title": "Idempotency-Key",
+                            "type": "string"
+                        }
+                    }
+                ],
+                "responses": {
+                    "204": {
+                        "description": "Successful Response"
+                    },
+                    "401": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Unauthorized"
+                    },
+                    "403": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Forbidden"
+                    },
+                    "404": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Not Found"
+                    },
+                    "409": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Conflict"
+                    },
+                    "422": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                        "description": "Validation Error"
+                    },
+                    "429": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HttpErrorOut"
+                                }
+                            }
+                        },
+                        "description": "Too Many Requests"
+                    }
+                },
+                "security": [
+                    {
+                        "HTTPBearer": []
+                    }
+                ],
+                "summary": "Delete message payload",
+                "tags": [
+                    "Message"
+                ],
+                "x-codeSamples": [
+                    {
+                        "label": "JavaScript",
+                        "lang": "JavaScript",
+                        "source": "await svix.message.expungeContent('app_id', 'msg_id');"
+                    },
+                    {
+                        "label": "TypeScript",
+                        "lang": "JavaScript",
+                        "source": "await svix.message.expungeContent('app_id', 'msg_id');"
+                    },
+                    {
+                        "label": "Python",
+                        "lang": "Python",
+                        "source": "svix.message.expunge_content('app_id', 'msg_id')"
+                    },
+                    {
+                        "label": "Python (Async)",
+                        "lang": "Python",
+                        "source": "await svix.message.expunge_content('app_id', 'msg_id')"
+                    },
+                    {
+                        "label": "Go",
+                        "lang": "Go",
+                        "source": "err := svixClient.Message.ExpungeContent(\"app_id\", \"msg_id\")"
+                    },
+                    {
+                        "label": "Kotlin",
+                        "lang": "Kotlin",
+                        "source": "svix.message.expungeContent('app_id', 'msg_id')"
+                    },
+                    {
+                        "label": "Java",
+                        "lang": "Java",
+                        "source": "svix.getMessage().expungeContent('app_id', 'msg_id')"
+                    },
+                    {
+                        "label": "Ruby",
+                        "lang": "Ruby",
+                        "source": "svix.message.expunge_content('app_id', 'msg_id')"
+                    },
+                    {
+                        "label": "Rust",
+                        "lang": "Rust",
+                        "source": "svix.message().expunge_content(\"app_id\", \"msg_id\").await?;"
+                    },
+                    {
+                        "label": "C#",
+                        "lang": "C#",
+                        "source": "await svix.Message.ExpungeContentAsync(\"app_id\", \"msg_id\")"
+                    },
+                    {
+                        "label": "CLI",
+                        "lang": "Shell",
+                        "source": "svix message expunge-content 'app_id' 'msg_id'"
+                    },
+                    {
+                        "label": "cURL",
+                        "lang": "Shell",
+                        "source": "curl -X 'DELETE' \\\n  'https://api.svix.com/api/v1/app/{app_id}/msg/{msg_id}/content/' \\\n  -H 'Authorization: Bearer AUTH_TOKEN' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json'"
+                    }
+                ]
+            }
+        },
         "/api/v1/app/{app_id}/msg/{msg_id}/endpoint/": {
             "get": {
                 "description": "`msg_id`: Use a message id or a message `eventId`",
@@ -11715,62 +12151,62 @@
                     {
                         "label": "JavaScript",
                         "lang": "JavaScript",
-                        "source": "const appPortalAccessOut = await svix.authentication.appPortalAccess('app_id', {\n    featureFlags: None\n});"
+                        "source": "const appPortalAccessOut = await svix.authentication.appPortalAccess('app_id', {\n    featureFlags: [    ]\n});"
                     },
                     {
                         "label": "TypeScript",
                         "lang": "JavaScript",
-                        "source": "const appPortalAccessOut = await svix.authentication.appPortalAccess('app_id', {\n    featureFlags: None\n});"
+                        "source": "const appPortalAccessOut = await svix.authentication.appPortalAccess('app_id', {\n    featureFlags: [    ]\n});"
                     },
                     {
                         "label": "Python",
                         "lang": "Python",
-                        "source": "app_portal_access_out = svix.authentication.app_portal_access('app_id', AppPortalAccessIn(\n    feature_flags=None\n))"
+                        "source": "app_portal_access_out = svix.authentication.app_portal_access('app_id', AppPortalAccessIn(\n    feature_flags=[]\n))"
                     },
                     {
                         "label": "Python (Async)",
                         "lang": "Python",
-                        "source": "app_portal_access_out = await svix.authentication.app_portal_access('app_id', AppPortalAccessIn(\n    feature_flags=None\n))"
+                        "source": "app_portal_access_out = await svix.authentication.app_portal_access('app_id', AppPortalAccessIn(\n    feature_flags=[]\n))"
                     },
                     {
                         "label": "Go",
                         "lang": "Go",
-                        "source": "appPortalAccessOut, err := svixClient.Authentication.AppPortalAccess(\"app_id\", &svix.AppPortalAccessIn{\n    FeatureFlags: None\n})"
+                        "source": "appPortalAccessOut, err := svixClient.Authentication.AppPortalAccess(\"app_id\", &svix.AppPortalAccessIn{\n    FeatureFlags: [...]string{}\n})"
                     },
                     {
                         "label": "Kotlin",
                         "lang": "Kotlin",
-                        "source": "val appPortalAccessOut = svix.authentication.appPortalAccess('app_id', AppPortalAccessIn()\n    .featureFlags(None)\n)"
+                        "source": "val appPortalAccessOut = svix.authentication.appPortalAccess('app_id', AppPortalAccessIn()\n    .featureFlags(arrayOf())\n)"
                     },
                     {
                         "label": "Java",
                         "lang": "Java",
-                        "source": "AppPortalAccessOut appPortalAccessOut = svix.getAuthentication().appPortalAccess('app_id', new AppPortalAccessIn()\n    .featureFlags(None)\n)"
+                        "source": "AppPortalAccessOut appPortalAccessOut = svix.getAuthentication().appPortalAccess('app_id', new AppPortalAccessIn()\n    .featureFlags(new String[]{})\n)"
                     },
                     {
                         "label": "Ruby",
                         "lang": "Ruby",
-                        "source": "app_portal_access_out = svix.authentication.app_portal_access('app_id', Svix::AppPortalAccessIn.new({\n    \"feature_flags\": null\n}))"
+                        "source": "app_portal_access_out = svix.authentication.app_portal_access('app_id', Svix::AppPortalAccessIn.new({\n    \"feature_flags\": []\n}))"
                     },
                     {
                         "label": "Rust",
                         "lang": "Rust",
-                        "source": "let app_portal_access_out = svix.authentication().app_portal_access(\"app_id\", AppPortalAccessIn {\n    feature_flags: None\n}).await?;"
+                        "source": "let app_portal_access_out = svix.authentication().app_portal_access(\"app_id\", AppPortalAccessIn {\n    feature_flags: Some(vec![])\n}).await?;"
                     },
                     {
                         "label": "C#",
                         "lang": "C#",
-                        "source": "var appPortalAccessOut = await svix.Authentication.AppPortalAccessAsync(\"app_id\", new AppPortalAccessIn{\n    featureFlags: None\n})"
+                        "source": "var appPortalAccessOut = await svix.Authentication.AppPortalAccessAsync(\"app_id\", new AppPortalAccessIn{\n    featureFlags: new string[] {}\n})"
                     },
                     {
                         "label": "CLI",
                         "lang": "Shell",
-                        "source": "svix authentication app-portal-access 'app_id' '{\n    \"featureFlags\": null\n}'"
+                        "source": "svix authentication app-portal-access 'app_id' '{\n    \"featureFlags\": []\n}'"
                     },
                     {
                         "label": "cURL",
                         "lang": "Shell",
-                        "source": "curl -X 'POST' \\\n  'https://api.svix.com/api/v1/auth/app-portal-access/{app_id}/' \\\n  -H 'Authorization: Bearer AUTH_TOKEN' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"featureFlags\": null\n}'"
+                        "source": "curl -X 'POST' \\\n  'https://api.svix.com/api/v1/auth/app-portal-access/{app_id}/' \\\n  -H 'Authorization: Bearer AUTH_TOKEN' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"featureFlags\": []\n}'"
                     }
                 ]
             }
@@ -12065,7 +12501,7 @@
                         "HTTPBearer": []
                     }
                 ],
-                "summary": "Get Consumer App Portal Access",
+                "summary": "Get Dashboard Access",
                 "tags": [
                     "Authentication"
                 ],
@@ -13174,7 +13610,7 @@
         },
         "/api/v1/event-type/{event_type_name}/": {
             "delete": {
-                "description": "Archive an event type.\n\nEndpoints already configured to filter on an event type will continue to do so after archival.\nHowever, new messages can not be sent with it and endpoints can not filter on it.\nAn event type can be unarchived with the\n[create operation](#operation/create_event_type_api_v1_event_type__post).",
+                "description": "Archive an event type.\n\nEndpoints already configured to filter on an event type will continue to do so after archival.\nHowever, new messages can not be sent with it and endpoints can not filter on it.\nAn event type can be unarchived with the\n[create operation](#operation/create_event_type_api_v1_event_type__post).\n\nIf `expunge=true` is set then the event type is deleted instead of archived.\nThis can only be used in development environments.",
                 "operationId": "delete_event_type_api_v1_event_type__event_type_name___delete",
                 "parameters": [
                     {
@@ -13189,6 +13625,16 @@
                             "type": "string"
                         }
                     },
+                    {
+                        "in": "query",
+                        "name": "expunge",
+                        "required": false,
+                        "schema": {
+                            "default": false,
+                            "title": "Expunge",
+                            "type": "boolean"
+                        }
+                    },
                     {
                         "description": "The request's idempotency key",
                         "in": "header",
diff --git a/python/svix/__init__.py b/python/svix/__init__.py
index 97436c915..f5c4c2328 100644
--- a/python/svix/__init__.py
+++ b/python/svix/__init__.py
@@ -37,4 +37,4 @@
     "WebhookVerificationError",
 ]
 
-__version__ = "0.75.0"
+__version__ = "0.81.0"
diff --git a/python/svix/api.py b/python/svix/api.py
index 8979b2f80..3d0d09640 100644
--- a/python/svix/api.py
+++ b/python/svix/api.py
@@ -23,10 +23,13 @@
     get_endpoint_headers_api_v1_app_app_id_endpoint_endpoint_id_headers_get,
     get_endpoint_secret_api_v1_app_app_id_endpoint_endpoint_id_secret_get,
     get_endpoint_stats_api_v1_app_app_id_endpoint_endpoint_id_stats_get,
+    get_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_get,
     list_endpoints_api_v1_app_app_id_endpoint_get,
     patch_endpoint_headers_api_v1_app_app_id_endpoint_endpoint_id_headers_patch,
     recover_failed_webhooks_api_v1_app_app_id_endpoint_endpoint_id_recover_post,
+    replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post,
     rotate_endpoint_secret_api_v1_app_app_id_endpoint_endpoint_id_secret_rotate_post,
+    set_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_patch,
     update_endpoint_api_v1_app_app_id_endpoint_endpoint_id_put,
     update_endpoint_headers_api_v1_app_app_id_endpoint_endpoint_id_headers_put,
 )
@@ -48,10 +51,12 @@
 )
 from .internal.openapi_client.api.message import (
     create_message_api_v1_app_app_id_msg_post,
+    expunge_message_payload_api_v1_app_app_id_msg_msg_id_content_delete,
     get_message_api_v1_app_app_id_msg_msg_id_get,
     list_messages_api_v1_app_app_id_msg_get,
 )
 from .internal.openapi_client.api.message_attempt import (
+    expunge_attempt_content_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_content_delete,
     get_attempt_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_get,
     list_attempted_destinations_api_v1_app_app_id_msg_msg_id_endpoint_get,
     list_attempted_messages_api_v1_app_app_id_endpoint_endpoint_id_msg_get,
@@ -75,6 +80,8 @@
 from .internal.openapi_client.models.endpoint_secret_out import EndpointSecretOut
 from .internal.openapi_client.models.endpoint_secret_rotate_in import EndpointSecretRotateIn
 from .internal.openapi_client.models.endpoint_stats import EndpointStats
+from .internal.openapi_client.models.endpoint_transformation_in import EndpointTransformationIn
+from .internal.openapi_client.models.endpoint_transformation_out import EndpointTransformationOut
 from .internal.openapi_client.models.endpoint_update import EndpointUpdate
 from .internal.openapi_client.models.event_type_in import EventTypeIn
 from .internal.openapi_client.models.event_type_out import EventTypeOut
@@ -100,8 +107,11 @@
 from .internal.openapi_client.models.message_out import MessageOut
 from .internal.openapi_client.models.message_out_payload import MessageOutPayload
 from .internal.openapi_client.models.message_status import MessageStatus
+from .internal.openapi_client.models.ordering import Ordering
 from .internal.openapi_client.models.recover_in import RecoverIn
 from .internal.openapi_client.models.recover_out import RecoverOut
+from .internal.openapi_client.models.replay_in import ReplayIn
+from .internal.openapi_client.models.replay_out import ReplayOut
 from .internal.openapi_client.models.status_code_class import StatusCodeClass
 
 DEFAULT_SERVER_URL = "https://api.svix.com"
@@ -140,7 +150,7 @@ class MessageListOptions(ListOptions):
 
 @dataclass
 class ApplicationListOptions(ListOptions):
-    pass
+    order: t.Optional[Ordering] = None
 
 
 @dataclass
@@ -151,7 +161,7 @@ class EventTypeListOptions(ListOptions):
 
 @dataclass
 class EndpointListOptions(ListOptions):
-    pass
+    order: t.Optional[Ordering] = None
 
 
 @dataclass
@@ -356,6 +366,28 @@ async def patch_headers(self, app_id: str, endpoint_id: str, endpoint_headers_in
             json_body=endpoint_headers_in,
         )
 
+    async def replay_missing(
+        self, app_id: str, endpoint_id: str, replay_in: ReplayIn, options: PostOptions = PostOptions()
+    ) -> ReplayOut:
+        return await replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post.asyncio(
+            client=self._client, app_id=app_id, endpoint_id=endpoint_id, json_body=replay_in, **options.to_dict()
+        )
+
+    async def transformations_get(self, app_id: str, endpoint_id: str) -> EndpointTransformationOut:
+        return await get_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_get.asyncio(
+            client=self._client, app_id=app_id, endpoint_id=endpoint_id
+        )
+
+    async def transformation_partial_update(
+        self, app_id: str, endpoint_id: str, endpoint_transformation_in: EndpointTransformationIn
+    ) -> None:
+        await set_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_patch.asyncio(
+            client=self._client,
+            app_id=app_id,
+            endpoint_id=endpoint_id,
+            json_body=endpoint_transformation_in,
+        )
+
 
 class Endpoint(ApiBase):
     def list(self, app_id: str, options: EndpointListOptions = EndpointListOptions()) -> ListResponseEndpointOut:
@@ -456,6 +488,28 @@ def get_stats(self, app_id: str, endpoint_id: str) -> EndpointStats:
             endpoint_id=endpoint_id,
         )
 
+    def replay_missing(
+        self, app_id: str, endpoint_id: str, replay_in: ReplayIn, options: PostOptions = PostOptions()
+    ) -> ReplayOut:
+        return replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post.sync(
+            client=self._client, app_id=app_id, endpoint_id=endpoint_id, json_body=replay_in, **options.to_dict()
+        )
+
+    def transformations_get(self, app_id: str, endpoint_id: str) -> EndpointTransformationOut:
+        return get_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_get.sync(
+            client=self._client, app_id=app_id, endpoint_id=endpoint_id
+        )
+
+    def transformation_partial_update(
+        self, app_id: str, endpoint_id: str, endpoint_transformation_in: EndpointTransformationIn
+    ) -> None:
+        set_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_patch.sync(
+            client=self._client,
+            app_id=app_id,
+            endpoint_id=endpoint_id,
+            json_body=endpoint_transformation_in,
+        )
+
 
 class EventTypeAsync(ApiBase):
     async def list(self, options: EventTypeListOptions = EventTypeListOptions()) -> ListResponseEventTypeOut:
@@ -665,6 +719,13 @@ async def get(self, app_id: str, msg_id: str) -> MessageOut:
             msg_id=msg_id,
         )
 
+    async def expunge_content(self, app_id: str, msg_id: str) -> None:
+        return await expunge_message_payload_api_v1_app_app_id_msg_msg_id_content_delete.asyncio(
+            client=self._client,
+            app_id=app_id,
+            msg_id=msg_id,
+        )
+
 
 class Message(ApiBase):
     def list(self, app_id: str, options: MessageListOptions = MessageListOptions()) -> ListResponseMessageOut:
@@ -692,6 +753,13 @@ def get(self, app_id: str, msg_id: str) -> MessageOut:
             msg_id=msg_id,
         )
 
+    def expunge_content(self, app_id: str, msg_id: str) -> None:
+        return expunge_message_payload_api_v1_app_app_id_msg_msg_id_content_delete.sync(
+            client=self._client,
+            app_id=app_id,
+            msg_id=msg_id,
+        )
+
 
 class MessageAttemptAsync(ApiBase):
     async def list_by_msg(
@@ -760,6 +828,19 @@ async def list_attempts_for_endpoint(
             **options.to_dict(),
         )
 
+    async def expunge_content(
+        self,
+        app_id: str,
+        msg_id: str,
+        attempt_id: str,
+    ) -> None:
+        return await expunge_attempt_content_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_content_delete.asyncio(
+            client=self._client,
+            app_id=app_id,
+            msg_id=msg_id,
+            attempt_id=attempt_id,
+        )
+
 
 class MessageAttempt(ApiBase):
     @deprecated(reason="use list_by_msg or list_by_endpoint instead")
@@ -834,6 +915,19 @@ def list_attempts_for_endpoint(
             **options.to_dict(),
         )
 
+    def expunge_content(
+        self,
+        app_id: str,
+        msg_id: str,
+        attempt_id: str,
+    ) -> None:
+        return expunge_attempt_content_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_content_delete.sync(
+            client=self._client,
+            app_id=app_id,
+            msg_id=msg_id,
+            attempt_id=attempt_id,
+        )
+
 
 class ClientBase:
     _client: AuthenticatedClient
diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock
index d8742bb48..58fb3accd 100644
--- a/ruby/Gemfile.lock
+++ b/ruby/Gemfile.lock
@@ -1,7 +1,7 @@
 PATH
   remote: .
   specs:
-    svix (0.75.0)
+    svix (0.81.0)
       typhoeus (~> 1.0, >= 1.0.1)
 
 GEM
diff --git a/ruby/lib/svix.rb b/ruby/lib/svix.rb
index 0f1d511a3..9c0a9335a 100644
--- a/ruby/lib/svix.rb
+++ b/ruby/lib/svix.rb
@@ -16,6 +16,8 @@
 require "svix/api/message_attempt_api"
 
 # Models
+require "svix/models/app_portal_access_in"
+require "svix/models/app_portal_access_out"
 require "svix/models/application_in"
 require "svix/models/application_out"
 require "svix/models/dashboard_access_out"
diff --git a/ruby/lib/svix/endpoint_api.rb b/ruby/lib/svix/endpoint_api.rb
index 305d94173..9cb4414fb 100644
--- a/ruby/lib/svix/endpoint_api.rb
+++ b/ruby/lib/svix/endpoint_api.rb
@@ -55,5 +55,18 @@ def get_stats(app_id, endpoint_id)
       return @api.get_endpoint_stats_api_v1_app_app_id_endpoint_endpoint_id_stats_get(endpoint_id app_id)
     end
 
+    def replay_missing(app_id, endpoint_id, replay_in, options = {})
+      @api.replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post(app_id, endpoint_id, replay_in, options)
+      nil
+    end
+
+    def transformation_get(app_id, endpoint_id, options = {})
+      return @api.get_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_get(endpoint_id, app_id, options)
+    end
+
+    def transformation_partial_update(app_id, endpoint_id, endpoint_transformation_in, options = {})
+      @api.set_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_patch(app_id, endpoint_id, endpoint_transformation_in, options)
+      nil
+    end
   end
 end
diff --git a/ruby/lib/svix/message_api.rb b/ruby/lib/svix/message_api.rb
index 4d1a580c1..c9d629901 100644
--- a/ruby/lib/svix/message_api.rb
+++ b/ruby/lib/svix/message_api.rb
@@ -1,21 +1,25 @@
 # frozen_string_literal: true
 
 module Svix
-    class MessageAPI
-        def initialize(api_client)
-            @api = MessageApi.new(api_client)
-        end
+  class MessageAPI
+    def initialize(api_client)
+      @api = MessageApi.new(api_client)
+    end
 
-        def list(app_id, options = {})
-            return @api.list_messages_api_v1_app_app_id_msg_get(app_id, options)
-        end
+    def list(app_id, options = {})
+      return @api.list_messages_api_v1_app_app_id_msg_get(app_id, options)
+    end
 
-        def create(app_id, message_in, options = {})
-            return @api.create_message_api_v1_app_app_id_msg_post(app_id, message_in, options)
-        end
+    def create(app_id, message_in, options = {})
+      return @api.create_message_api_v1_app_app_id_msg_post(app_id, message_in, options)
+    end
+
+    def get(app_id, msg_id)
+      return @api.get_message_api_v1_app_app_id_msg_msg_id_get(msg_id, app_id)
+    end
 
-        def get(app_id, msg_id)
-            return @api.get_message_api_v1_app_app_id_msg_msg_id_get(msg_id, app_id)
-        end
+    def expunge_content(app_id, msg_id)
+      return @api.expunge_message_payload_api_v1_app_app_id_msg_msg_id_content_delete(msg_id, app_id)
     end
+  end
 end
diff --git a/ruby/lib/svix/message_attempt_api.rb b/ruby/lib/svix/message_attempt_api.rb
index e57b74998..775d5e4c2 100644
--- a/ruby/lib/svix/message_attempt_api.rb
+++ b/ruby/lib/svix/message_attempt_api.rb
@@ -1,43 +1,47 @@
 # frozen_string_literal: true
 
 module Svix
-    class MessageAttemptAPI
-        def initialize(api_client)
-            @api = MessageAttemptApi.new(api_client)
-        end
+  class MessageAttemptAPI
+    def initialize(api_client)
+      @api = MessageAttemptApi.new(api_client)
+    end
 
-        # <b>DEPRECATED:</b> Please use <tt>list_by_msg</tt> or <tt>list_by_endpoint</tt> instead.
-        def list(app_id, msg_id, options = {})
-            warn "[DEPRECATION] `list` is deprecated.  Please use `list_by_msg` or `list_by_endpoint` instead."
-            return self.list_by_msg(app_id, msg_id, options)
-        end
+    # <b>DEPRECATED:</b> Please use <tt>list_by_msg</tt> or <tt>list_by_endpoint</tt> instead.
+    def list(app_id, msg_id, options = {})
+      warn "[DEPRECATION] `list` is deprecated.  Please use `list_by_msg` or `list_by_endpoint` instead."
+      return self.list_by_msg(app_id, msg_id, options)
+    end
 
-        def list_by_msg(app_id, msg_id, options = {})
-            return @api.list_attempts_by_msg_api_v1_app_app_id_attempt_msg_msg_id_get(app_id, msg_id, options)
-        end
+    def list_by_msg(app_id, msg_id, options = {})
+      return @api.list_attempts_by_msg_api_v1_app_app_id_attempt_msg_msg_id_get(app_id, msg_id, options)
+    end
+
+    def list_by_endpoint(app_id, endpoint_id, options = {})
+      return @api.list_attempts_by_endpoint_api_v1_app_app_id_attempt_endpoint_endpoint_id_get(app_id, endpoint_id, options)
+    end
 
-        def list_by_endpoint(app_id, endpoint_id, options = {})
-            return @api.list_attempts_by_endpoint_api_v1_app_app_id_attempt_endpoint_endpoint_id_get(app_id, endpoint_id, options)
-        end
+    def get(app_id, msg_id, attempt_id)
+      return @api.get_attempt_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_get(attempt_id, msg_id, app_id)
+    end
 
-        def get(app_id, msg_id, attempt_id)
-            return @api.get_attempt_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_get(attempt_id, msg_id, app_id)
-        end
+    def resend(app_id, msg_id, endpoint_id, options = {})
+      return @api.resend_webhook_api_v1_app_app_id_msg_msg_id_endpoint_endpoint_id_resend_post(endpoint_id, msg_id, app_id, options)
+    end
 
-        def resend(app_id, msg_id, endpoint_id, options = {})
-            return @api.resend_webhook_api_v1_app_app_id_msg_msg_id_endpoint_endpoint_id_resend_post(endpoint_id, msg_id, app_id, options)
-        end
+    def list_attempted_messages(app_id, endpoint_id, options = {})
+      return @api.list_attempted_messages_api_v1_app_app_id_endpoint_endpoint_id_msg_get(endpoint_id, app_id, options)
+    end
 
-        def list_attempted_messages(app_id, endpoint_id, options = {})
-            return @api.list_attempted_messages_api_v1_app_app_id_endpoint_endpoint_id_msg_get(endpoint_id, app_id, options)
-        end
+    def list_attempted_destinations(app_id, msg_id, options = {})
+      return @api.list_attempted_destinations_api_v1_app_app_id_msg_msg_id_endpoint_get(msg_id, app_id, options)
+    end
 
-        def list_attempted_destinations(app_id, msg_id, options = {})
-            return @api.list_attempted_destinations_api_v1_app_app_id_msg_msg_id_endpoint_get(msg_id, app_id, options)
-        end
+    def list_attempts_for_endpoint(app_id, endpoint_id, msg_id, options = {})
+      return @api.list_attempts_for_endpoint_api_v1_app_app_id_msg_msg_id_endpoint_endpoint_id_attempt_get(msg_id, app_id, endpoint_id, options)
+    end
 
-        def list_attempts_for_endpoint(app_id, endpoint_id, msg_id, options = {})
-            return @api.list_attempts_for_endpoint_api_v1_app_app_id_msg_msg_id_endpoint_endpoint_id_attempt_get(msg_id, app_id, endpoint_id, options)
-        end
+    def expunge_content(app_id, msg_id, attempt_id)
+      return @api.expunge_attempt_content_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_content_delete(attempt_id, msg_id, app_id)
     end
+  end
 end
diff --git a/ruby/lib/svix/version.rb b/ruby/lib/svix/version.rb
index e43c3bd00..672d3ef09 100644
--- a/ruby/lib/svix/version.rb
+++ b/ruby/lib/svix/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 module Svix
-  VERSION = "0.75.0"
+  VERSION = "0.81.0"
 end
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index cc5a5d175..91e666d78 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "svix"
-version = "0.75.0"
+version = "0.81.0"
 authors = ["Svix Inc. <oss@svix.com>"]
 edition = "2021"
 description="Rust library for interacting with the Svix API and verifying webhook signatures."
diff --git a/rust/src/api.rs b/rust/src/api.rs
index 822268cb0..b5aa40427 100644
--- a/rust/src/api.rs
+++ b/rust/src/api.rs
@@ -4,19 +4,7 @@ use crate::apis::{
     message_api, message_attempt_api,
 };
 use crate::error::Result;
-use crate::models::{
-    AppPortalAccessIn, AppPortalAccessOut, IntegrationIn, IntegrationKeyOut, IntegrationOut,
-    IntegrationUpdate, ListResponseIntegrationOut,
-};
-pub use crate::models::{
-    ApplicationIn, ApplicationOut, DashboardAccessOut, EndpointHeadersIn, EndpointHeadersOut,
-    EndpointHeadersPatchIn, EndpointIn, EndpointOut, EndpointSecretOut, EndpointSecretRotateIn,
-    EndpointStats, EndpointUpdate, EventTypeIn, EventTypeOut, EventTypeUpdate,
-    ListResponseApplicationOut, ListResponseEndpointMessageOut, ListResponseEndpointOut,
-    ListResponseEventTypeOut, ListResponseMessageAttemptEndpointOut, ListResponseMessageAttemptOut,
-    ListResponseMessageEndpointOut, ListResponseMessageOut, MessageAttemptOut, MessageIn,
-    MessageOut, MessageStatus, RecoverIn, StatusCodeClass,
-};
+pub use crate::models::*;
 
 const CRATE_VERSION: &str = env!("CARGO_PKG_VERSION");
 
@@ -148,7 +136,12 @@ pub struct ListOptions {
     pub limit: Option<i32>,
 }
 
-pub type ApplicationListOptions = ListOptions;
+#[derive(Default)]
+pub struct ApplicationListOptions {
+    pub iterator: Option<String>,
+    pub limit: Option<i32>,
+    pub order: Option<Ordering>,
+}
 
 pub struct Application<'a> {
     cfg: &'a Configuration,
@@ -163,12 +156,17 @@ impl<'a> Application<'a> {
         &self,
         options: Option<ApplicationListOptions>,
     ) -> Result<ListResponseApplicationOut> {
-        let ApplicationListOptions { iterator, limit } = options.unwrap_or_default();
+        let ApplicationListOptions {
+            iterator,
+            limit,
+            order,
+        } = options.unwrap_or_default();
         Ok(application_api::list_applications_api_v1_app_get(
             self.cfg,
             application_api::ListApplicationsApiV1AppGetParams {
                 iterator,
                 limit,
+                order,
                 idempotency_key: None,
             },
         )
@@ -252,7 +250,12 @@ impl<'a> Application<'a> {
     }
 }
 
-pub type EndpointListOptions = ListOptions;
+#[derive(Default)]
+pub struct EndpointListOptions {
+    pub iterator: Option<String>,
+    pub limit: Option<i32>,
+    pub order: Option<Ordering>,
+}
 
 pub struct Endpoint<'a> {
     cfg: &'a Configuration,
@@ -268,11 +271,16 @@ impl<'a> Endpoint<'a> {
         app_id: String,
         options: Option<EndpointListOptions>,
     ) -> Result<ListResponseEndpointOut> {
-        let EndpointListOptions { iterator, limit } = options.unwrap_or_default();
+        let EndpointListOptions {
+            iterator,
+            limit,
+            order,
+        } = options.unwrap_or_default();
         Ok(endpoint_api::list_endpoints_api_v1_app_app_id_endpoint_get(
             self.cfg,
             endpoint_api::ListEndpointsApiV1AppAppIdEndpointGetParams {
                 app_id,
+                order,
                 iterator,
                 limit,
                 idempotency_key: None,
@@ -479,6 +487,58 @@ impl<'a> Endpoint<'a> {
             .await?,
         )
     }
+
+    pub async fn replay_missing(
+        &self,
+        app_id: String,
+        endpoint_id: String,
+        replay_in: ReplayIn,
+        options: Option<PostOptions>,
+    ) -> Result<()> {
+        let PostOptions { idempotency_key } = options.unwrap_or_default();
+        endpoint_api::replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post(
+            self.cfg,
+            endpoint_api::ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostParams{
+                app_id,
+                endpoint_id,
+                replay_in,
+                idempotency_key,
+            },
+        )
+        .await?;
+        Ok(())
+    }
+
+    pub async fn transformation_get(
+        &self,
+        app_id: String,
+        endpoint_id: String,
+    ) -> Result<EndpointTransformationOut> {
+        Ok(endpoint_api::get_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_get(
+            self.cfg,
+            endpoint_api::GetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationGetParams {
+                app_id,
+                endpoint_id,
+                idempotency_key: None,
+            }
+        )
+        .await?)
+    }
+
+    pub async fn transformation_partial_update(
+        &self,
+        app_id: String,
+        endpoint_id: String,
+        endpoint_transformation_in: EndpointTransformationIn,
+    ) -> Result<()> {
+        endpoint_api::set_endpoint_transformation_api_v1_app_app_id_endpoint_endpoint_id_transformation_patch(self.cfg, endpoint_api::SetEndpointTransformationApiV1AppAppIdEndpointEndpointIdTransformationPatchParams {
+            app_id,
+            endpoint_id,
+            endpoint_transformation_in,
+            idempotency_key: None,
+        }).await?;
+        Ok(())
+    }
 }
 
 pub type IntegrationListOptions = ListOptions;
@@ -706,6 +766,7 @@ impl<'a> EventType<'a> {
                 self.cfg,
                 event_type_api::DeleteEventTypeApiV1EventTypeEventTypeNameDeleteParams {
                     event_type_name,
+                    expunge: None,
                     idempotency_key: None,
                 },
             )
@@ -795,6 +856,20 @@ impl<'a> Message<'a> {
         )
         .await?)
     }
+
+    pub async fn expunge_content(&self, app_id: String, msg_id: String) -> Result<()> {
+        Ok(
+            message_api::expunge_message_payload_api_v1_app_app_id_msg_msg_id_content_delete(
+                self.cfg,
+                message_api::ExpungeMessagePayloadApiV1AppAppIdMsgMsgIdContentDeleteParams {
+                    msg_id,
+                    app_id,
+                    idempotency_key: None,
+                },
+            )
+            .await?,
+        )
+    }
 }
 
 #[derive(Default)]
@@ -1023,4 +1098,24 @@ impl<'a> MessageAttempt<'a> {
             .await?,
         )
     }
+
+    pub async fn expunge_content(
+        &self,
+        app_id: String,
+        msg_id: String,
+        attempt_id: String,
+    ) -> Result<()> {
+        Ok(
+            message_attempt_api::expunge_attempt_content_api_v1_app_app_id_msg_msg_id_attempt_attempt_id_content_delete(
+                self.cfg,
+                message_attempt_api::ExpungeAttemptContentApiV1AppAppIdMsgMsgIdAttemptAttemptIdContentDeleteParams {
+                    app_id,
+                    msg_id,
+                    attempt_id,
+                    idempotency_key: None,
+                },
+            )
+            .await?
+        )
+    }
 }
diff --git a/server/Cargo.lock b/server/Cargo.lock
index 11fb614c5..7de59c3ac 100644
--- a/server/Cargo.lock
+++ b/server/Cargo.lock
@@ -30,9 +30,9 @@ dependencies = [
 
 [[package]]
 name = "ahash"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
+checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
 dependencies = [
  "cfg-if",
  "getrandom",
@@ -100,25 +100,26 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.66"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
 
 [[package]]
 name = "async-stream"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
+checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e"
 dependencies = [
  "async-stream-impl",
  "futures-core",
+ "pin-project-lite",
 ]
 
 [[package]]
 name = "async-stream-impl"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
+checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -127,9 +128,9 @@ dependencies = [
 
 [[package]]
 name = "async-trait"
-version = "0.1.59"
+version = "0.1.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
+checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -154,17 +155,6 @@ dependencies = [
  "autocfg 1.1.0",
 ]
 
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
 [[package]]
 name = "autocfg"
 version = "0.1.8"
@@ -182,9 +172,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "axum"
-version = "0.6.1"
+version = "0.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08b108ad2665fa3f6e6a517c3d80ec3e77d224c47d605167aefaa5d7ef97fa48"
+checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591"
 dependencies = [
  "async-trait",
  "axum-core",
@@ -216,9 +206,9 @@ dependencies = [
 
 [[package]]
 name = "axum-core"
-version = "0.3.0"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79b8558f5a0581152dc94dcd289132a1d377494bdeafcd41869b3258e3e2ad92"
+checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e"
 dependencies = [
  "async-trait",
  "bytes",
@@ -262,6 +252,12 @@ version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
+[[package]]
+name = "base64"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
+
 [[package]]
 name = "base64ct"
 version = "1.1.1"
@@ -315,9 +311,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "blake2"
-version = "0.10.5"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
 dependencies = [
  "digest 0.10.6",
 ]
@@ -342,9 +338,9 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.11.1"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
 
 [[package]]
 name = "bytecount"
@@ -360,15 +356,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
 
 [[package]]
 name = "cc"
-version = "1.0.77"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
 
 [[package]]
 name = "cfg-if"
@@ -428,30 +424,13 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "3.2.23"
+version = "4.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
+checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
 dependencies = [
- "atty",
  "bitflags",
- "clap_derive 3.2.18",
- "clap_lex 0.2.4",
- "indexmap",
- "once_cell",
- "strsim",
- "termcolor",
- "textwrap",
-]
-
-[[package]]
-name = "clap"
-version = "4.0.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
-dependencies = [
- "bitflags",
- "clap_derive 4.0.21",
- "clap_lex 0.3.0",
+ "clap_derive",
+ "clap_lex",
  "is-terminal",
  "once_cell",
  "strsim",
@@ -460,24 +439,11 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "3.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
-dependencies = [
- "heck 0.4.0",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.0.21"
+version = "4.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
+checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
 dependencies = [
- "heck 0.4.0",
+ "heck 0.4.1",
  "proc-macro-error",
  "proc-macro2",
  "quote",
@@ -486,27 +452,18 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
-dependencies = [
- "os_str_bytes",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.3.0"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
+checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
 dependencies = [
  "os_str_bytes",
 ]
 
 [[package]]
 name = "coarsetime"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "454038500439e141804c655b4cd1bc6a70bcb95cd2bc9463af5661b6956f0e46"
+checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354"
 dependencies = [
  "libc",
  "once_cell",
@@ -535,7 +492,7 @@ dependencies = [
  "memchr",
  "pin-project-lite",
  "tokio",
- "tokio-util 0.7.4",
+ "tokio-util 0.7.7",
 ]
 
 [[package]]
@@ -577,18 +534,18 @@ dependencies = [
 
 [[package]]
 name = "crc"
-version = "3.0.0"
+version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
+checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
 dependencies = [
  "crc-catalog",
 ]
 
 [[package]]
 name = "crc-catalog"
-version = "2.1.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff"
+checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
 
 [[package]]
 name = "crc16"
@@ -598,9 +555,9 @@ checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff"
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.6"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -618,9 +575,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.14"
+version = "0.8.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
+checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
 dependencies = [
  "cfg-if",
 ]
@@ -676,9 +633,9 @@ checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"
 
 [[package]]
 name = "cxx"
-version = "1.0.82"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
+checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72"
 dependencies = [
  "cc",
  "cxxbridge-flags",
@@ -688,9 +645,9 @@ dependencies = [
 
 [[package]]
 name = "cxx-build"
-version = "1.0.82"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
+checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613"
 dependencies = [
  "cc",
  "codespan-reporting",
@@ -703,15 +660,15 @@ dependencies = [
 
 [[package]]
 name = "cxxbridge-flags"
-version = "1.0.82"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
+checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "1.0.82"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
+checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -720,9 +677,9 @@ dependencies = [
 
 [[package]]
 name = "darling"
-version = "0.14.2"
+version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
+checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8"
 dependencies = [
  "darling_core",
  "darling_macro",
@@ -730,9 +687,9 @@ dependencies = [
 
 [[package]]
 name = "darling_core"
-version = "0.14.2"
+version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
+checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb"
 dependencies = [
  "fnv",
  "ident_case",
@@ -744,9 +701,9 @@ dependencies = [
 
 [[package]]
 name = "darling_macro"
-version = "0.14.2"
+version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
+checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"
 dependencies = [
  "darling_core",
  "quote",
@@ -755,9 +712,9 @@ dependencies = [
 
 [[package]]
 name = "data-encoding"
-version = "2.3.2"
+version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
+checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
 
 [[package]]
 name = "der"
@@ -833,9 +790,9 @@ checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0"
 
 [[package]]
 name = "dyn-clone"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60"
+checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
 
 [[package]]
 name = "ecdsa"
@@ -861,9 +818,9 @@ dependencies = [
 
 [[package]]
 name = "either"
-version = "1.8.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
 [[package]]
 name = "elliptic-curve"
@@ -886,9 +843,9 @@ dependencies = [
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.31"
+version = "0.8.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
+checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
 dependencies = [
  "cfg-if",
 ]
@@ -899,7 +856,7 @@ version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116"
 dependencies = [
- "heck 0.4.0",
+ "heck 0.4.1",
  "proc-macro2",
  "quote",
  "syn",
@@ -907,9 +864,9 @@ dependencies = [
 
 [[package]]
 name = "enum_dispatch"
-version = "0.3.8"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb"
+checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2"
 dependencies = [
  "once_cell",
  "proc-macro2",
@@ -956,9 +913,9 @@ dependencies = [
 
 [[package]]
 name = "fastrand"
-version = "1.8.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
 dependencies = [
  "instant",
 ]
@@ -1027,9 +984,9 @@ dependencies = [
 
 [[package]]
 name = "fraction"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99df8100674344d1cee346c764684f7ad688a4dcaa1a3efb2fdb45daf9acf4f9"
+checksum = "7aa5de57a62c2440ece64342ea59efb7171aa7d016faf8dfcb8795066a17146b"
 dependencies = [
  "lazy_static",
  "num",
@@ -1037,9 +994,9 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
+checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1052,9 +1009,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
+checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1062,15 +1019,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
+checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
+checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1090,15 +1047,15 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
+checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
+checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1107,21 +1064,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
+checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
 
 [[package]]
 name = "futures-task"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
+checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
 
 [[package]]
 name = "futures-util"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
+checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1169,9 +1126,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.3.15"
+version = "0.3.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
+checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
 dependencies = [
  "bytes",
  "fnv",
@@ -1182,7 +1139,7 @@ dependencies = [
  "indexmap",
  "slab",
  "tokio",
- "tokio-util 0.7.4",
+ "tokio-util 0.7.7",
  "tracing",
 ]
 
@@ -1210,7 +1167,7 @@ version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584"
 dependencies = [
- "base64",
+ "base64 0.13.1",
  "bitflags",
  "bytes",
  "headers-core",
@@ -1240,30 +1197,27 @@ dependencies = [
 
 [[package]]
 name = "heck"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 dependencies = [
  "unicode-segmentation",
 ]
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "hermit-abi"
-version = "0.2.6"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
-dependencies = [
- "libc",
-]
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
 
 [[package]]
 name = "hex"
@@ -1336,9 +1290,9 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
 dependencies = [
  "bytes",
  "fnv",
@@ -1417,9 +1371,9 @@ dependencies = [
 
 [[package]]
 name = "hyper-rustls"
-version = "0.23.1"
+version = "0.23.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d"
+checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
 dependencies = [
  "http",
  "hyper",
@@ -1525,12 +1479,12 @@ dependencies = [
 
 [[package]]
 name = "io-lifetimes"
-version = "1.0.3"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
+checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
 dependencies = [
  "libc",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1547,23 +1501,23 @@ dependencies = [
 
 [[package]]
 name = "ipnet"
-version = "2.5.1"
+version = "2.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
+checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "is-terminal"
-version = "0.4.1"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
+checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
 dependencies = [
- "hermit-abi 0.2.6",
+ "hermit-abi 0.3.1",
  "io-lifetimes",
  "rustix",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1586,15 +1540,15 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.4"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
 
 [[package]]
 name = "js-sys"
-version = "0.3.60"
+version = "0.3.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -1605,11 +1559,11 @@ version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ca9e2b45609132ae2214d50482c03aeee78826cd6fd53a8940915b81acedf16"
 dependencies = [
- "ahash 0.8.2",
+ "ahash 0.8.3",
  "anyhow",
- "base64",
+ "base64 0.13.1",
  "bytecount",
- "clap 4.0.29",
+ "clap",
  "fancy-regex",
  "fraction",
  "iso8601",
@@ -1623,7 +1577,7 @@ dependencies = [
  "reqwest",
  "serde",
  "serde_json",
- "time 0.3.17",
+ "time 0.3.20",
  "url",
  "uuid",
 ]
@@ -1675,9 +1629,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.137"
+version = "0.2.139"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
 
 [[package]]
 name = "libm"
@@ -1687,9 +1641,9 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
 
 [[package]]
 name = "link-cplusplus"
-version = "1.0.7"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
 dependencies = [
  "cc",
 ]
@@ -1711,9 +1665,9 @@ dependencies = [
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
 
 [[package]]
 name = "lock_api"
@@ -1760,9 +1714,9 @@ dependencies = [
 
 [[package]]
 name = "matches"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
 
 [[package]]
 name = "matchit"
@@ -1809,14 +1763,14 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "mio"
-version = "0.8.5"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
 dependencies = [
  "libc",
  "log",
  "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1845,9 +1799,9 @@ dependencies = [
 
 [[package]]
 name = "nom"
-version = "7.1.1"
+version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
  "memchr",
  "minimal-lexical",
@@ -1914,9 +1868,9 @@ checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa"
 
 [[package]]
 name = "num-complex"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
+checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
 dependencies = [
  "num-traits",
 ]
@@ -1966,28 +1920,28 @@ dependencies = [
 
 [[package]]
 name = "num_cpus"
-version = "1.14.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
 dependencies = [
- "hermit-abi 0.1.19",
+ "hermit-abi 0.2.6",
  "libc",
 ]
 
 [[package]]
 name = "num_enum"
-version = "0.5.7"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
+checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
 dependencies = [
  "num_enum_derive",
 ]
 
 [[package]]
 name = "num_enum_derive"
-version = "0.5.7"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
+checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -1997,9 +1951,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.16.0"
+version = "1.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
 [[package]]
 name = "opaque-debug"
@@ -2111,9 +2065,9 @@ checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
 
 [[package]]
 name = "ouroboros"
-version = "0.15.5"
+version = "0.15.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbb50b356159620db6ac971c6d5c9ab788c9cc38a6f49619fca2a27acb062ca"
+checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db"
 dependencies = [
  "aliasable",
  "ouroboros_macro",
@@ -2121,9 +2075,9 @@ dependencies = [
 
 [[package]]
 name = "ouroboros_macro"
-version = "0.15.5"
+version = "0.15.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a0d9d1a6191c4f391f87219d1ea42b23f09ee84d64763cd05ee6ea88d9f384d"
+checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7"
 dependencies = [
  "Inflector",
  "proc-macro-error",
@@ -2158,7 +2112,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 dependencies = [
  "instant",
  "lock_api",
- "parking_lot_core 0.8.5",
+ "parking_lot_core 0.8.6",
 ]
 
 [[package]]
@@ -2168,14 +2122,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 dependencies = [
  "lock_api",
- "parking_lot_core 0.9.5",
+ "parking_lot_core 0.9.7",
 ]
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.5"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
 dependencies = [
  "cfg-if",
  "instant",
@@ -2187,22 +2141,22 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.5"
+version = "0.9.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
 dependencies = [
  "cfg-if",
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
 name = "paste"
-version = "1.0.9"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
+checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
 
 [[package]]
 name = "pear"
@@ -2253,9 +2207,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
 
 [[package]]
 name = "petgraph"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
+checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
 dependencies = [
  "fixedbitset",
  "indexmap",
@@ -2353,13 +2307,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro-crate"
-version = "1.2.1"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
 dependencies = [
  "once_cell",
- "thiserror",
- "toml",
+ "toml_edit",
 ]
 
 [[package]]
@@ -2388,9 +2341,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.47"
+version = "1.0.51"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
 dependencies = [
  "unicode-ident",
 ]
@@ -2469,9 +2422,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
 name = "quote"
-version = "1.0.21"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
 dependencies = [
  "proc-macro2",
 ]
@@ -2508,9 +2461,9 @@ dependencies = [
 
 [[package]]
 name = "redis"
-version = "0.21.6"
+version = "0.21.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "571c252c68d09a2ad3e49edd14e9ee48932f3e0f27b06b4ea4c9b2a706d31103"
+checksum = "152f3863635cbb76b73bc247845781098302c6c9ad2060e1a9a7de56840346b6"
 dependencies = [
  "async-trait",
  "bytes",
@@ -2524,7 +2477,7 @@ dependencies = [
  "sha1 0.6.1",
  "tokio",
  "tokio-native-tls",
- "tokio-util 0.7.4",
+ "tokio-util 0.7.7",
  "url",
 ]
 
@@ -2564,9 +2517,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.7.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -2588,22 +2541,13 @@ version = "0.6.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
 
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "reqwest"
-version = "0.11.13"
+version = "0.11.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
+checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
 dependencies = [
- "base64",
+ "base64 0.21.0",
  "bytes",
  "encoding_rs",
  "futures-core",
@@ -2696,23 +2640,23 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.36.4"
+version = "0.36.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
+checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
 dependencies = [
  "bitflags",
  "errno",
  "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
 name = "rustls"
-version = "0.20.7"
+version = "0.20.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
+checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
 dependencies = [
  "log",
  "ring",
@@ -2722,40 +2666,39 @@ dependencies = [
 
 [[package]]
 name = "rustls-pemfile"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
+checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
 dependencies = [
- "base64",
+ "base64 0.21.0",
 ]
 
 [[package]]
 name = "rustversion"
-version = "1.0.9"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
+checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
 
 [[package]]
 name = "ryu"
-version = "1.0.11"
+version = "1.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
 
 [[package]]
 name = "schannel"
-version = "0.1.20"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
+checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
 dependencies = [
- "lazy_static",
- "windows-sys 0.36.1",
+ "windows-sys 0.42.0",
 ]
 
 [[package]]
 name = "schemars"
-version = "0.8.11"
+version = "0.8.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
+checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
 dependencies = [
  "chrono",
  "dyn-clone",
@@ -2768,9 +2711,9 @@ dependencies = [
 
 [[package]]
 name = "schemars_derive"
-version = "0.8.11"
+version = "0.8.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
+checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2786,9 +2729,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "scratch"
-version = "1.0.2"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
+checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
 
 [[package]]
 name = "sct"
@@ -2802,9 +2745,9 @@ dependencies = [
 
 [[package]]
 name = "sea-orm"
-version = "0.10.4"
+version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3120bc435b8640963ffda698f877610e07e077157e216eb99408d819c344034d"
+checksum = "88694d01b528a94f90ad87f8d2f546d060d070eee180315c67d158cb69476034"
 dependencies = [
  "async-stream",
  "async-trait",
@@ -2821,16 +2764,16 @@ dependencies = [
  "serde_json",
  "sqlx",
  "thiserror",
- "time 0.3.17",
+ "time 0.3.20",
  "tracing",
  "url",
 ]
 
 [[package]]
 name = "sea-orm-macros"
-version = "0.10.4"
+version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c54bacfeb842813c16821e21f9456c358861a448294075184ea1d6307e386d08"
+checksum = "7216195de9c6b2474fd0efab486173dccd0eff21f28cc54aa4c0205d52fb3af0"
 dependencies = [
  "bae",
  "heck 0.3.3",
@@ -2912,9 +2855,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework"
-version = "2.7.0"
+version = "2.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
+checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -2925,9 +2868,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.6.1"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
+checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -2935,18 +2878,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.148"
+version = "1.0.152"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.148"
+version = "1.0.152"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2966,9 +2909,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.89"
+version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
+checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
 dependencies = [
  "itoa",
  "ryu",
@@ -2977,9 +2920,9 @@ dependencies = [
 
 [[package]]
 name = "serde_path_to_error"
-version = "0.1.8"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d"
+checksum = "db0969fff533976baadd92e08b1d102c5a3d8a8049eadfd69d4d1e3c5b2ed189"
 dependencies = [
  "serde",
 ]
@@ -3057,9 +3000,9 @@ dependencies = [
 
 [[package]]
 name = "signal-hook-registry"
-version = "1.4.0"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
 dependencies = [
  "libc",
 ]
@@ -3076,9 +3019,9 @@ dependencies = [
 
 [[package]]
 name = "slab"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
 dependencies = [
  "autocfg 1.1.0",
 ]
@@ -3091,9 +3034,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
 
 [[package]]
 name = "socket2"
-version = "0.4.7"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
 dependencies = [
  "libc",
  "winapi",
@@ -3126,9 +3069,9 @@ dependencies = [
 
 [[package]]
 name = "sqlformat"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a"
+checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e"
 dependencies = [
  "itertools",
  "nom",
@@ -3153,7 +3096,7 @@ checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105"
 dependencies = [
  "ahash 0.7.6",
  "atoi",
- "base64",
+ "base64 0.13.1",
  "bitflags",
  "byteorder",
  "bytes",
@@ -3207,7 +3150,7 @@ checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9"
 dependencies = [
  "dotenvy",
  "either",
- "heck 0.4.0",
+ "heck 0.4.1",
  "once_cell",
  "proc-macro2",
  "quote",
@@ -3261,7 +3204,7 @@ version = "0.24.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
 dependencies = [
- "heck 0.4.0",
+ "heck 0.4.1",
  "proc-macro2",
  "quote",
  "rustversion",
@@ -3280,7 +3223,7 @@ version = "0.60.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e4a0071892c06482d6585e7b15146b088f777d4da18aa924d9f960d59b7e4f4"
 dependencies = [
- "base64",
+ "base64 0.13.1",
  "hmac-sha256",
  "http",
  "reqwest",
@@ -3288,7 +3231,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "thiserror",
- "time 0.3.17",
+ "time 0.3.20",
  "url",
 ]
 
@@ -3306,23 +3249,24 @@ dependencies = [
 
 [[package]]
 name = "svix-server"
-version = "0.75.0"
+version = "0.81.0"
 dependencies = [
  "aide",
  "anyhow",
  "axum",
- "base64",
+ "base64 0.13.1",
  "bb8",
  "bb8-redis",
  "blake2",
  "bytes",
  "chacha20poly1305",
  "chrono",
- "clap 3.2.23",
+ "clap",
  "dotenv",
  "ed25519-compact",
  "enum_dispatch",
  "figment",
+ "form_urlencoded",
  "futures",
  "hmac-sha256",
  "http",
@@ -3358,7 +3302,7 @@ dependencies = [
  "svix-ksuid",
  "svix-server_derive",
  "thiserror",
- "time 0.3.17",
+ "time 0.3.20",
  "tokio",
  "tower",
  "tower-http",
@@ -3382,9 +3326,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.104"
+version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3393,9 +3337,9 @@ dependencies = [
 
 [[package]]
 name = "sync_wrapper"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
 
 [[package]]
 name = "synstructure"
@@ -3411,47 +3355,40 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.3.0"
+version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
 dependencies = [
  "cfg-if",
  "fastrand",
- "libc",
  "redox_syscall",
- "remove_dir_all",
- "winapi",
+ "rustix",
+ "windows-sys 0.42.0",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
 dependencies = [
  "winapi-util",
 ]
 
-[[package]]
-name = "textwrap"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
-
 [[package]]
 name = "thiserror"
-version = "1.0.37"
+version = "1.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.37"
+version = "1.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3460,10 +3397,11 @@ dependencies = [
 
 [[package]]
 name = "thread_local"
-version = "1.1.4"
+version = "1.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
 dependencies = [
+ "cfg-if",
  "once_cell",
 ]
 
@@ -3480,9 +3418,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.17"
+version = "0.3.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
+checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
 dependencies = [
  "serde",
  "time-core",
@@ -3497,9 +3435,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
 
 [[package]]
 name = "time-macros"
-version = "0.2.6"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
+checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
 dependencies = [
  "time-core",
 ]
@@ -3515,15 +3453,15 @@ dependencies = [
 
 [[package]]
 name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.23.1"
+version = "1.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38a54aca0c15d014013256222ba0ebed095673f89345dd79119d912eb561b7a8"
+checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
 dependencies = [
  "autocfg 1.1.0",
  "bytes",
@@ -3536,7 +3474,7 @@ dependencies = [
  "signal-hook-registry",
  "socket2",
  "tokio-macros",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -3551,9 +3489,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-macros"
-version = "1.8.0"
+version = "1.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
+checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3562,9 +3500,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-native-tls"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
 dependencies = [
  "native-tls",
  "tokio",
@@ -3595,9 +3533,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.11"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
+checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -3620,9 +3558,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.4"
+version = "0.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
+checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
 dependencies = [
  "bytes",
  "futures-core",
@@ -3634,13 +3572,30 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.5.9"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
 dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "toml_datetime"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
+
+[[package]]
+name = "toml_edit"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow",
+]
+
 [[package]]
 name = "tonic"
 version = "0.6.2"
@@ -3649,7 +3604,7 @@ checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a"
 dependencies = [
  "async-stream",
  "async-trait",
- "base64",
+ "base64 0.13.1",
  "bytes",
  "futures-core",
  "futures-util",
@@ -3698,7 +3653,7 @@ dependencies = [
  "rand",
  "slab",
  "tokio",
- "tokio-util 0.7.4",
+ "tokio-util 0.7.7",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -3706,9 +3661,9 @@ dependencies = [
 
 [[package]]
 name = "tower-http"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba"
+checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
 dependencies = [
  "bitflags",
  "bytes",
@@ -3884,15 +3839,15 @@ dependencies = [
 
 [[package]]
 name = "try-lock"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
 
 [[package]]
 name = "typenum"
-version = "1.15.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
 
 [[package]]
 name = "uncased"
@@ -3914,15 +3869,15 @@ dependencies = [
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.8"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.5"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
 
 [[package]]
 name = "unicode-normalization"
@@ -3935,9 +3890,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.10.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
 
 [[package]]
 name = "unicode-width"
@@ -3993,9 +3948,9 @@ checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
 
 [[package]]
 name = "uuid"
-version = "1.2.2"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
+checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
 dependencies = [
  "getrandom",
 ]
@@ -4085,9 +4040,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -4095,9 +4050,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
 dependencies = [
  "bumpalo",
  "log",
@@ -4110,9 +4065,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.33"
+version = "0.4.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -4122,9 +4077,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -4132,9 +4087,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4145,15 +4100,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
 
 [[package]]
 name = "web-sys"
-version = "0.3.60"
+version = "0.3.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -4171,18 +4126,18 @@ dependencies = [
 
 [[package]]
 name = "webpki-roots"
-version = "0.22.5"
+version = "0.22.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
+checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
 dependencies = [
  "webpki",
 ]
 
 [[package]]
 name = "which"
-version = "4.3.0"
+version = "4.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
 dependencies = [
  "either",
  "libc",
@@ -4191,11 +4146,10 @@ dependencies = [
 
 [[package]]
 name = "whoami"
-version = "1.2.3"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571"
+checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3"
 dependencies = [
- "bumpalo",
  "wasm-bindgen",
  "web-sys",
 ]
@@ -4237,19 +4191,6 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
-[[package]]
-name = "windows-sys"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
-dependencies = [
- "windows_aarch64_msvc 0.36.1",
- "windows_i686_gnu 0.36.1",
- "windows_i686_msvc 0.36.1",
- "windows_x86_64_gnu 0.36.1",
- "windows_x86_64_msvc 0.36.1",
-]
-
 [[package]]
 name = "windows-sys"
 version = "0.42.0"
@@ -4257,85 +4198,88 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
 dependencies = [
  "windows_aarch64_gnullvm",
- "windows_aarch64_msvc 0.42.0",
- "windows_i686_gnu 0.42.0",
- "windows_i686_msvc 0.42.0",
- "windows_x86_64_gnu 0.42.0",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
  "windows_x86_64_gnullvm",
- "windows_x86_64_msvc 0.42.0",
+ "windows_x86_64_msvc",
 ]
 
 [[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.0"
+name = "windows-sys"
+version = "0.45.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets",
+]
 
 [[package]]
-name = "windows_aarch64_msvc"
-version = "0.36.1"
+name = "windows-targets"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
 
 [[package]]
-name = "windows_aarch64_msvc"
-version = "0.42.0"
+name = "windows_aarch64_gnullvm"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
 
 [[package]]
-name = "windows_i686_gnu"
-version = "0.36.1"
+name = "windows_aarch64_msvc"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.36.1"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.36.1"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.36.1"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
 
 [[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.0"
+name = "winnow"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+checksum = "c95fb4ff192527911dd18eb138ac30908e7165b8944e528b6af93aa4c842d345"
+dependencies = [
+ "memchr",
+]
 
 [[package]]
 name = "winreg"
@@ -4363,9 +4307,9 @@ dependencies = [
 
 [[package]]
 name = "zeroize_derive"
-version = "1.3.2"
+version = "1.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
+checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/server/svix-server/Cargo.toml b/server/svix-server/Cargo.toml
index d82012a41..cf91970d7 100644
--- a/server/svix-server/Cargo.toml
+++ b/server/svix-server/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "svix-server"
-version = "0.75.0"
+version = "0.81.0"
 license = "MIT"
 description = "Svix webhooks server"
 
@@ -19,17 +19,17 @@ svix-ksuid = "^0.5.1"
 dotenv = "0.15.0"
 sha2 = "0.10.2"
 hmac-sha256 = "1"
-clap = { version = "3.2.1", features = ["derive"] }
+clap = { version = "4.1.8", features = ["derive"] }
 axum = { version = "0.6.1", features = ["headers"] }
 base64 = "0.13.0"
 hyper = { version = "0.14.16", features = ["full"] }
 hyper-openssl = "0.9.2"
 openssl = "0.10.45"
-tokio = { version = "1.23.1", features = ["full"] }
+tokio = { version = "1.24.2", features = ["full"] }
 tower = "0.4.11"
 tower-http = { version = "0.3.4", features = ["trace", "cors", "request-id"] }
 serde = { version = "1.0.133", features = ["derive"] }
-serde_json = "1.0.74"
+serde_json = { version = "1.0.74", features = ["arbitrary_precision"] }
 serde_urlencoded = "0.7.1"
 serde_path_to_error = "0.1.7"
 num_enum = "0.5.6"
@@ -74,6 +74,7 @@ ipnet = { version = "2.5", features = ["serde"] }
 urlencoding = "2.1.2"
 strum_macros = "0.24"
 strum = { version = "0.24", features = ["derive"] }
+form_urlencoded = "1.1.0"
 
 [dev-dependencies]
 anyhow = "1.0.56"
diff --git a/server/svix-server/src/core/message_app.rs b/server/svix-server/src/core/message_app.rs
index ae1041fd4..0111bd564 100644
--- a/server/svix-server/src/core/message_app.rs
+++ b/server/svix-server/src/core/message_app.rs
@@ -33,8 +33,8 @@ pub struct CreateMessageApp {
     pub uid: Option<ApplicationUid>,
     pub org_id: OrganizationId,
     pub rate_limit: Option<u16>,
-    pub endpoints: Vec<CreateMessageEndpoint>,
-    pub deleted: bool,
+    endpoints: Vec<CreateMessageEndpoint>,
+    deleted: bool,
 }
 
 impl CreateMessageApp {
@@ -77,8 +77,12 @@ impl CreateMessageApp {
         let cache_key = AppEndpointKey::new(&org_id, &app_id);
 
         // First check Redis
-        if let Ok(Some(cma)) = cache.get(&cache_key).await {
-            return Ok(Some(cma));
+        if let Ok(Some(cma)) = cache.get::<CreateMessageApp>(&cache_key).await {
+            if cma.deleted {
+                return Ok(None);
+            } else {
+                return Ok(Some(cma));
+            }
         }
 
         // Then check PostgreSQL
@@ -102,6 +106,10 @@ impl CreateMessageApp {
         // Insert it into Redis
         let _ = cache.set(&cache_key, &out, ttl).await;
 
+        if out.deleted {
+            return Ok(None);
+        }
+
         Ok(Some(out))
     }
 
@@ -148,7 +156,6 @@ pub struct CreateMessageEndpoint {
     pub id: EndpointId,
     pub url: String,
     pub key: EndpointSecretInternal,
-    pub old_signing_keys: Option<ExpiringSigningKeys>,
     pub event_types_ids: Option<EventTypeNameSet>,
     pub channels: Option<EventChannelSet>,
     pub rate_limit: Option<u16>,
@@ -157,6 +164,8 @@ pub struct CreateMessageEndpoint {
     pub headers: Option<EndpointHeaders>,
     pub disabled: bool,
     pub deleted: bool,
+    // outside of this module, valid_signing_keys should be used instead
+    old_signing_keys: Option<ExpiringSigningKeys>,
 }
 
 impl CreateMessageEndpoint {
diff --git a/server/svix-server/src/core/types/mod.rs b/server/svix-server/src/core/types/mod.rs
index 5446f5cc0..140e0a22f 100644
--- a/server/svix-server/src/core/types/mod.rs
+++ b/server/svix-server/src/core/types/mod.rs
@@ -259,6 +259,13 @@ macro_rules! string_wrapper {
 
 macro_rules! string_wrapper_impl {
     ($name_id:ident) => {
+        impl $name_id {
+            /// Wraps the type as JSONB. Useful when doing comparisons in a jsonb container w/sea_orm|postgres.
+            pub fn jsonb(self) -> sea_orm::Value {
+                sea_orm::Value::Json(Some(Box::new(serde_json::Value::String(self.0))))
+            }
+        }
+
         impl Deref for $name_id {
             type Target = String;
 
@@ -318,6 +325,12 @@ macro_rules! string_wrapper_impl {
                 self.0.fmt(f)
             }
         }
+
+        impl From<String> for $name_id {
+            fn from(s: String) -> Self {
+                $name_id(s)
+            }
+        }
     };
 }
 
@@ -347,12 +360,6 @@ macro_rules! create_id_type {
                 )))
             }
         }
-
-        impl From<String> for $name_id {
-            fn from(s: String) -> Self {
-                $name_id(s)
-            }
-        }
     };
 }
 
diff --git a/server/svix-server/src/core/webhook_http_client.rs b/server/svix-server/src/core/webhook_http_client.rs
index 197d1d7c4..93d5649ec 100644
--- a/server/svix-server/src/core/webhook_http_client.rs
+++ b/server/svix-server/src/core/webhook_http_client.rs
@@ -38,7 +38,7 @@ pub enum Error {
     #[error("failure response: {0}")]
     FailureStatus(StatusCode),
 
-    #[error("making requests to local IP addresses is forbidden and blocked")]
+    #[error("requests to this IP range are blocked (see the server configuration)")]
     BlockedIp,
     #[error("error resolving name: {0}")]
     ResolveError(#[from] ResolveError),
@@ -71,6 +71,8 @@ impl WebhookClient {
         ));
         connector.enforce_http(false);
 
+        // Openssl is required here -- in practice, rustls does not support many
+        // ciphers that we encounter on a regular basis:
         let ssl = SslConnector::builder(SslMethod::tls()).expect("SslConnector build failed");
         let https = HttpsConnector::with_connector(NonLocalConnector { connector }, ssl)
             .expect("HttpsConnector build failed");
@@ -137,9 +139,9 @@ impl WebhookClient {
                 match tokio::time::timeout(timeout, self.client.request(req)).await {
                     Ok(Ok(resp)) => Ok(resp),
                     Ok(Err(e)) => Err({
-                        if e.to_string().contains(
-                            "making requests to local IP addresses is forbidden and blocked",
-                        ) {
+                        if e.to_string()
+                            .contains("requests to this IP range are blocked")
+                        {
                             Error::BlockedIp
                         } else {
                             Error::FailedRequest(e)
@@ -150,7 +152,7 @@ impl WebhookClient {
             } else {
                 self.client.request(req).await.map_err(|e| {
                     if e.to_string()
-                        .contains("making requests to local IP addresses is forbidden and blocked")
+                        .contains("requests to this IP range are blocked")
                     {
                         Error::BlockedIp
                     } else {
diff --git a/server/svix-server/src/db/models/application.rs b/server/svix-server/src/db/models/application.rs
index 443ce5209..f9d7b2c6f 100644
--- a/server/svix-server/src/db/models/application.rs
+++ b/server/svix-server/src/db/models/application.rs
@@ -7,8 +7,8 @@ use crate::core::types::{
 use crate::{ctx, error};
 use chrono::Utc;
 use sea_orm::ActiveValue::Set;
+use sea_orm::ConnectionTrait;
 use sea_orm::{entity::prelude::*, Condition};
-use sea_orm::{ConnectionTrait, QueryOrder, QuerySelect};
 
 use super::applicationmetadata;
 
@@ -67,34 +67,6 @@ impl Model {
         Ok(metadata)
     }
 
-    pub async fn fetch_many_with_metadata(
-        db: &DatabaseConnection,
-        org_id: OrganizationId,
-        limit: u64,
-        after_id: impl Into<Option<ApplicationId>>,
-    ) -> error::Result<impl Iterator<Item = (Self, applicationmetadata::Model)>> {
-        let mut query = Entity::secure_find(org_id)
-            .order_by_asc(Column::Id)
-            .limit(limit);
-
-        if let Some(id) = after_id.into() {
-            query = query.filter(Column::Id.gt(id))
-        }
-
-        let results = ctx!(
-            query
-                .find_also_related(applicationmetadata::Entity)
-                .all(db)
-                .await
-        )?;
-
-        Ok(results.into_iter().map(|(app, metadata)| {
-            let metadata =
-                metadata.unwrap_or_else(|| applicationmetadata::Model::new(app.id.clone()));
-            (app, metadata)
-        }))
-    }
-
     pub async fn fetch_with_metadata(
         db: &DatabaseConnection,
         org_id: OrganizationId,
diff --git a/server/svix-server/src/error.rs b/server/svix-server/src/error.rs
index 5636bebb5..a56ad2551 100644
--- a/server/svix-server/src/error.rs
+++ b/server/svix-server/src/error.rs
@@ -21,7 +21,6 @@ use sea_orm::RuntimeErr;
 use sea_orm::TransactionError;
 use serde::Serialize;
 use serde_json::json;
-use sqlx::Error as SqlxError;
 
 use crate::core::webhook_http_client;
 
@@ -188,34 +187,7 @@ impl<T> Traceable<T> for Result<T> {
 
 impl<T> Traceable<T> for std::result::Result<T, DbErr> {
     fn trace(self, location: &'static str) -> Result<T> {
-        self.map_err(|err| {
-            let typ = if let DbErr::Query(runtime_error) = err {
-                match runtime_error {
-                    RuntimeErr::SqlxError(sqlx_err) => {
-                        if matches!(sqlx_err, SqlxError::RowNotFound) {
-                            HttpError::not_found(None, None).into()
-                        } else if sqlx_err
-                            .as_database_error()
-                            .and_then(|e| e.code())
-                            .filter(|code| code == "23505") // "duplicate key value violates unique constraint"
-                            .is_some()
-                        {
-                            HttpError::conflict(None, None).into()
-                        } else {
-                            ErrorType::Database(sqlx_err.to_string())
-                        }
-                    }
-                    RuntimeErr::Internal(err) => ErrorType::Database(err),
-                }
-            } else {
-                ErrorType::Database(err.to_string())
-            };
-
-            Error {
-                trace: Error::init_trace(location),
-                typ,
-            }
-        })
+        self.map_err(|e| Error::database(e, location))
     }
 }
 
@@ -455,3 +427,22 @@ impl From<crate::core::webhook_http_client::Error> for Error {
         }
     }
 }
+
+/// Utility function for Converting a [`DbErr`] into an [`HttpError`] (wrapped by [`Error`]) on the
+/// error "duplicate key value violates unique constraint". This is to be used in `map_err` calls
+/// on creation/update of records
+pub fn http_error_on_conflict(db_err: DbErr) -> Error {
+    match db_err {
+        DbErr::Query(RuntimeErr::SqlxError(e))
+            if e.as_database_error()
+                .and_then(|e| e.code())
+                .filter(|code| code == "23505")
+                .is_some() =>
+        {
+            HttpError::conflict(None, None).into()
+        }
+        // This always inserts a blank locaton, but because it should be wrapped by a [`ctx`] call,
+        // you should still get a meaningful trace
+        e => Error::database(e, ""),
+    }
+}
diff --git a/server/svix-server/src/expired_message_cleaner.rs b/server/svix-server/src/expired_message_cleaner.rs
index c396603ad..8b2dd5123 100644
--- a/server/svix-server/src/expired_message_cleaner.rs
+++ b/server/svix-server/src/expired_message_cleaner.rs
@@ -3,33 +3,72 @@
 
 use std::sync::atomic::Ordering;
 
-use crate::db::models::message;
 use crate::error::Result;
-use chrono::Utc;
-use sea_orm::{
-    entity::prelude::*, sea_query::Expr, DatabaseConnection, DbErr, EntityTrait, UpdateResult,
-};
+use sea_orm::{ConnectionTrait, DatabaseConnection, DbErr, Statement, UpdateResult};
+use std::time::Duration;
 use tokio::time::sleep;
 
-/// Nullifies the payload column for expired messages
+/// Nullifies the payload column for expired messages,
+/// `limit` sets how many rows to update at a time.
 pub async fn clean_expired_messages(
     pool: &DatabaseConnection,
+    limit: u32,
 ) -> std::result::Result<UpdateResult, DbErr> {
-    message::Entity::update_many()
-        .col_expr(message::Column::Payload, Expr::value(Value::Json(None)))
-        .filter(message::Column::Expiration.lte(Utc::now()))
-        .filter(message::Column::Payload.is_not_null())
-        .exec(pool)
-        .await
+    let stmt = Statement::from_sql_and_values(
+        pool.get_database_backend(),
+        r#"
+        UPDATE message SET payload = NULL WHERE id IN (
+            SELECT id FROM message
+            WHERE
+                expiration <= now()
+                AND payload IS NOT NULL
+            LIMIT $1
+            FOR UPDATE SKIP LOCKED
+        )
+    "#,
+        [limit.into()],
+    );
+    let res = pool.execute(stmt).await?;
+    Ok(UpdateResult {
+        rows_affected: res.rows_affected(),
+    })
 }
 
-/// Runs every 10 seconds
+/// Polls the database for expired messages to nullify payloads for.
+///
+/// Uses a variable polling schedule, based on affected row counts each iteration of the loop.
 pub async fn expired_message_cleaner_loop(pool: &DatabaseConnection) -> Result<()> {
+    // When no rows have been updated, widen the interval.
+    const IDLE: Duration = Duration::from_secs(10);
+    // When the affected row count dips below this, switch to the `SLOWING` interval.
+    const SLOWING_THRESHOLD: u64 = 1_000;
+    const SLOWING: Duration = Duration::from_secs(3);
+    const BATCH_SIZE: u32 = 5_000;
+    let mut sleep_time = Some(IDLE);
     loop {
-        sleep(std::time::Duration::from_secs(10)).await;
+        if let Some(duration) = sleep_time {
+            sleep(duration).await;
+        }
         let pool = pool.clone();
-        if let Err(err) = clean_expired_messages(&pool).await {
-            tracing::error!("{}", err)
+        match clean_expired_messages(&pool, BATCH_SIZE).await {
+            Err(err) => {
+                tracing::error!("{}", err);
+            }
+            Ok(UpdateResult { rows_affected }) => {
+                if rows_affected > 0 {
+                    tracing::trace!("expired {} payloads", rows_affected);
+                }
+
+                sleep_time = match rows_affected {
+                    0 => Some(IDLE),
+                    count if count <= SLOWING_THRESHOLD => {
+                        tracing::trace!("slowing down...");
+                        Some(SLOWING)
+                    }
+                    // Any non-zero count above the slowing threshold gets no sleep.
+                    _ => None,
+                };
+            }
         }
 
         if crate::SHUTTING_DOWN.load(Ordering::SeqCst) {
diff --git a/server/svix-server/src/v1/endpoints/application.rs b/server/svix-server/src/v1/endpoints/application.rs
index 74fea25eb..da1dc63ba 100644
--- a/server/svix-server/src/v1/endpoints/application.rs
+++ b/server/svix-server/src/v1/endpoints/application.rs
@@ -4,26 +4,27 @@
 use crate::{
     core::{
         permissions,
-        types::{metadata::Metadata, ApplicationId, ApplicationIdOrUid, ApplicationUid},
+        types::{metadata::Metadata, ApplicationId, ApplicationUid},
     },
     ctx,
     db::models::{application, applicationmetadata},
-    error::{HttpError, Result},
+    error::{http_error_on_conflict, HttpError, Result},
     transaction,
     v1::utils::{
-        openapi_tag,
+        apply_pagination, openapi_tag,
         patch::{
             patch_field_non_nullable, patch_field_nullable, UnrequiredField,
             UnrequiredNullableField,
         },
         validate_no_control_characters, validate_no_control_characters_unrequired,
-        validation_error, EmptyResponse, ListResponse, ModelIn, ModelOut, Pagination,
-        PaginationLimit, ValidatedJson, ValidatedQuery,
+        validation_error, ApplicationPath, EmptyResponse, JsonStatus, JsonStatusUpsert,
+        ListOrdering, ListResponse, ModelIn, ModelOut, Pagination, PaginationLimit,
+        ReversibleIterator, ValidatedJson, ValidatedQuery,
     },
     AppState,
 };
 use aide::axum::{
-    routing::{get, post},
+    routing::{get_with, post_with},
     ApiRouter,
 };
 use axum::{
@@ -31,12 +32,11 @@ use axum::{
     Json,
 };
 use chrono::{DateTime, Utc};
-use hyper::StatusCode;
 use schemars::JsonSchema;
 use sea_orm::ActiveModelTrait;
 use sea_orm::ActiveValue::Set;
 use serde::{Deserialize, Serialize};
-use svix_server_derive::ModelOut;
+use svix_server_derive::{aide_annotate, ModelOut};
 use validator::{Validate, ValidationError};
 
 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Validate, JsonSchema)]
@@ -189,22 +189,43 @@ impl From<(application::Model, applicationmetadata::Model)> for ApplicationOut {
     }
 }
 
+/// List all of the organization's applications.
+#[aide_annotate(op_id = "list_applications_api_v1_app__get")]
 async fn list_applications(
     State(AppState { ref db, .. }): State<AppState>,
-    pagination: ValidatedQuery<Pagination<ApplicationId>>,
+    ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<ApplicationId>>>,
     permissions::Organization { org_id }: permissions::Organization,
 ) -> Result<Json<ListResponse<ApplicationOut>>> {
     let PaginationLimit(limit) = pagination.limit;
-    let iterator = pagination.iterator.clone();
-
-    let apps =
-        ctx!(application::Model::fetch_many_with_metadata(db, org_id, limit + 1, iterator).await)?;
-
-    let results = apps.map(ApplicationOut::from).collect();
-
-    Ok(Json(ApplicationOut::list_response_no_prev(
+    let iterator = pagination.iterator;
+    let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
+
+    let query = apply_pagination(
+        application::Entity::secure_find(org_id),
+        application::Column::Id,
+        limit,
+        iterator,
+        pagination.order.unwrap_or(ListOrdering::Ascending),
+    );
+
+    let results: Vec<ApplicationOut> = ctx!(
+        query
+            .find_also_related(applicationmetadata::Entity)
+            .all(db)
+            .await
+    )?
+    .into_iter()
+    .map(|(app, metadata)| {
+        let metadata = metadata.unwrap_or_else(|| applicationmetadata::Model::new(app.id.clone()));
+        (app, metadata)
+    })
+    .map(ApplicationOut::from)
+    .collect();
+
+    Ok(Json(ApplicationOut::list_response(
         results,
         limit as usize,
+        is_prev,
     )))
 }
 
@@ -218,18 +239,21 @@ pub struct CreateApplicationQuery {
     get_if_exists: bool,
 }
 
+/// Create a new application.
+#[aide_annotate(op_id = "create_application_api_v1_app__post")]
 async fn create_application(
     State(AppState { ref db, .. }): State<AppState>,
     query: ValidatedQuery<CreateApplicationQuery>,
     permissions::Organization { org_id }: permissions::Organization,
     ValidatedJson(data): ValidatedJson<ApplicationIn>,
-) -> Result<(StatusCode, Json<ApplicationOut>)> {
+) -> Result<JsonStatusUpsert<ApplicationOut>> {
     if let Some(ref uid) = data.uid {
         if let Some((app, metadata)) = ctx!(
             application::Model::fetch_with_metadata(db, org_id.clone(), uid.clone().into()).await
         )? {
             if query.get_if_exists {
-                return Ok((StatusCode::OK, Json((app, metadata).into())));
+                // Technically not updated, but it fits.
+                return Ok(JsonStatusUpsert::Updated((app, metadata).into()));
             }
             return Err(HttpError::conflict(
                 None,
@@ -247,26 +271,30 @@ async fn create_application(
     let (app, metadata) = model;
 
     let (app, metadata) = transaction!(db, |txn| async move {
-        let app_result = ctx!(app.insert(txn).await)?;
+        let app_result = ctx!(app.insert(txn).await.map_err(http_error_on_conflict))?;
         let metadata = ctx!(metadata.upsert_or_delete(txn).await)?;
         Ok((app_result, metadata))
     })?;
 
-    Ok((StatusCode::CREATED, Json((app, metadata).into())))
+    Ok(JsonStatusUpsert::Created((app, metadata).into()))
 }
 
+/// Get an application.
+#[aide_annotate(op_id = "get_application_api_v1_app__app_id___get")]
 async fn get_application(
     permissions::ApplicationWithMetadata { app, metadata }: permissions::ApplicationWithMetadata,
 ) -> Result<Json<ApplicationOut>> {
     Ok(Json((app, metadata).into()))
 }
 
+/// Update an application.
+#[aide_annotate(op_id = "update_application_api_v1_app__app_id___put")]
 async fn update_application(
     State(AppState { ref db, .. }): State<AppState>,
-    Path(app_id): Path<ApplicationIdOrUid>,
+    Path(ApplicationPath { app_id }): Path<ApplicationPath>,
     permissions::Organization { org_id }: permissions::Organization,
     ValidatedJson(data): ValidatedJson<ApplicationIn>,
-) -> Result<(StatusCode, Json<ApplicationOut>)> {
+) -> Result<JsonStatusUpsert<ApplicationOut>> {
     let (app, metadata, create_models) = if let Some((app, metadata)) =
         ctx!(application::Model::fetch_with_metadata(db, org_id.clone(), app_id).await)?
     {
@@ -283,22 +311,23 @@ async fn update_application(
 
     let (app, metadata) = transaction!(db, |txn| async move {
         let app = if create_models {
-            ctx!(app.insert(txn).await)?
+            ctx!(app.insert(txn).await.map_err(http_error_on_conflict))?
         } else {
-            ctx!(app.update(txn).await)?
+            ctx!(app.update(txn).await.map_err(http_error_on_conflict))?
         };
         let metadata = ctx!(metadata.upsert_or_delete(txn).await)?;
         Ok((app, metadata))
     })?;
 
-    let status = if create_models {
-        StatusCode::CREATED
+    if create_models {
+        Ok(JsonStatusUpsert::Created((app, metadata).into()))
     } else {
-        StatusCode::OK
-    };
-    Ok((status, Json((app, metadata).into())))
+        Ok(JsonStatusUpsert::Updated((app, metadata).into()))
+    }
 }
 
+/// Partially update an application.
+#[aide_annotate]
 async fn patch_application(
     State(AppState { ref db, .. }): State<AppState>,
     permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication,
@@ -312,7 +341,7 @@ async fn patch_application(
     let (app, metadata) = model;
 
     let (app, metadata) = transaction!(db, |txn| async move {
-        let app = ctx!(app.update(txn).await)?;
+        let app = ctx!(app.update(txn).await.map_err(http_error_on_conflict))?;
         let metadata = ctx!(metadata.upsert_or_delete(txn).await)?;
         Ok((app, metadata))
     })?;
@@ -320,31 +349,35 @@ async fn patch_application(
     Ok(Json((app, metadata).into()))
 }
 
+/// Delete an application.
+#[aide_annotate(op_id = "delete_application_api_v1_app__app_id___delete")]
 async fn delete_application(
     State(AppState { ref db, .. }): State<AppState>,
     permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<204, EmptyResponse>> {
     let mut app: application::ActiveModel = app.into();
     app.deleted = Set(true);
     app.uid = Set(None); // We don't want deleted UIDs to clash
     ctx!(app.update(db).await)?;
-    Ok((StatusCode::NO_CONTENT, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
 }
 
 pub fn router() -> ApiRouter<AppState> {
+    let tag = openapi_tag("Application");
     ApiRouter::new()
         .api_route_with(
             "/app/",
-            post(create_application).get(list_applications),
-            openapi_tag("Application"),
+            post_with(create_application, create_application_operation)
+                .get_with(list_applications, list_applications_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/",
-            get(get_application)
-                .put(update_application)
-                .patch(patch_application)
-                .delete(delete_application),
-            openapi_tag("Application"),
+            get_with(get_application, get_application_operation)
+                .put_with(update_application, update_application_operation)
+                .patch_with(patch_application, patch_application_operation)
+                .delete_with(delete_application, delete_application_operation),
+            tag,
         )
 }
 
diff --git a/server/svix-server/src/v1/endpoints/attempt.rs b/server/svix-server/src/v1/endpoints/attempt.rs
index f8fe84680..10399d071 100644
--- a/server/svix-server/src/v1/endpoints/attempt.rs
+++ b/server/svix-server/src/v1/endpoints/attempt.rs
@@ -5,9 +5,9 @@ use crate::{
     core::{
         permissions,
         types::{
-            ApplicationIdOrUid, EndpointId, EndpointIdOrUid, EventChannel, EventTypeNameSet,
-            MessageAttemptId, MessageAttemptTriggerType, MessageEndpointId, MessageId,
-            MessageIdOrUid, MessageStatus, StatusCodeClass,
+            EndpointId, EndpointIdOrUid, EventChannel, EventTypeNameSet, MessageAttemptId,
+            MessageAttemptTriggerType, MessageEndpointId, MessageId, MessageStatus,
+            StatusCodeClass,
         },
     },
     ctx,
@@ -18,15 +18,16 @@ use crate::{
     v1::{
         endpoints::message::MessageOut,
         utils::{
-            apply_pagination, iterator_from_before_or_after, openapi_tag, EmptyResponse,
-            ListResponse, MessageListFetchOptions, ModelOut, PaginationLimit, ReversibleIterator,
-            ValidatedQuery,
+            apply_pagination_desc, iterator_from_before_or_after, openapi_tag,
+            ApplicationEndpointPath, ApplicationMsgAttemptPath, ApplicationMsgEndpointPath,
+            ApplicationMsgPath, EmptyResponse, EventTypesQuery, JsonStatus, ListResponse, ModelOut,
+            PaginationLimit, ReversibleIterator, ValidatedQuery,
         },
     },
     AppState,
 };
 use aide::axum::{
-    routing::{get, post},
+    routing::{delete_with, get_with, post_with},
     ApiRouter,
 };
 use axum::{
@@ -37,10 +38,13 @@ use chrono::{DateTime, Utc};
 
 use hyper::StatusCode;
 use schemars::JsonSchema;
-use sea_orm::{entity::prelude::*, sea_query::Expr, DatabaseConnection, QueryOrder, QuerySelect};
+use sea_orm::{
+    entity::prelude::*, sea_query::Expr, DatabaseConnection, IntoActiveModel, QueryOrder,
+    QuerySelect,
+};
 use serde::{Deserialize, Serialize};
 
-use svix_server_derive::ModelOut;
+use svix_server_derive::{aide_annotate, ModelOut};
 use validator::Validate;
 
 use crate::db::models::messageattempt;
@@ -123,7 +127,12 @@ pub struct ListAttemptedMessagesQueryParameters {
     after: Option<DateTime<Utc>>,
 }
 
-/// Fetches a list of [`AttemptedMessageOut`]s associated with a given app and endpoint.
+/// List messages for a particular endpoint. Additionally includes metadata about the latest message attempt.
+///
+/// The `before` parameter lets you filter all items created before a certain date and is ignored if an iterator is passed.
+#[aide_annotate(
+    op_id = "list_attempted_messages_api_v1_app__app_id__endpoint__endpoint_id__msg__get"
+)]
 async fn list_attempted_messages(
     State(AppState { ref db, .. }): State<AppState>,
     ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<MessageId>>>,
@@ -133,12 +142,12 @@ async fn list_attempted_messages(
         before,
         after,
     }): ValidatedQuery<ListAttemptedMessagesQueryParameters>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<ListResponse<AttemptedMessageOut>>> {
     let PaginationLimit(limit) = pagination.limit;
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endpoint_id)
             .one(db)
             .await
     )?
@@ -149,7 +158,7 @@ async fn list_attempted_messages(
 
     if let Some(channel) = channel {
         dests_and_msgs =
-            dests_and_msgs.filter(Expr::cust_with_values("channels ?? ?", vec![channel]));
+            dests_and_msgs.filter(Expr::cust_with_values("channels @> $1", [channel.jsonb()]));
     }
 
     if let Some(status) = status {
@@ -181,7 +190,7 @@ async fn list_attempted_messages(
     let iterator = iterator_from_before_or_after(msg_dest_iterator, before, after);
     let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
 
-    let dests_and_msgs = apply_pagination(
+    let dests_and_msgs = apply_pagination_desc(
         dests_and_msgs,
         messagedestination::Column::Id,
         limit,
@@ -194,18 +203,10 @@ async fn list_attempted_messages(
         Ok(AttemptedMessageOut::from_dest_and_msg(dest, msg))
     };
 
-    let out = if is_prev {
-        ctx!(dests_and_msgs.all(db).await)?
-            .into_iter()
-            .rev()
-            .map(into)
-            .collect::<Result<_>>()?
-    } else {
-        ctx!(dests_and_msgs.all(db).await)?
-            .into_iter()
-            .map(into)
-            .collect::<Result<_>>()?
-    };
+    let out = ctx!(dests_and_msgs.all(db).await)?
+        .into_iter()
+        .map(into)
+        .collect::<Result<_>>()?;
 
     Ok(Json(AttemptedMessageOut::list_response(
         out,
@@ -221,8 +222,6 @@ pub struct ListAttemptsByEndpointQueryParameters {
     status: Option<MessageStatus>,
     status_code_class: Option<StatusCodeClass>,
     #[validate]
-    event_types: Option<EventTypeNameSet>,
-    #[validate]
     channel: Option<EventChannel>,
     before: Option<DateTime<Utc>>,
     after: Option<DateTime<Utc>>,
@@ -272,9 +271,9 @@ fn list_attempts_by_endpoint_or_message_filters(
     if event_types.is_some() || channel.is_some() {
         query = query.join_rev(
             sea_orm::JoinType::InnerJoin,
-            messageattempt::Entity::belongs_to(message::Entity)
-                .from(messageattempt::Column::MsgId)
-                .to(message::Column::Id)
+            message::Entity::belongs_to(messageattempt::Entity)
+                .from(message::Column::Id)
+                .to(messageattempt::Column::MsgId)
                 .into(),
         );
 
@@ -283,32 +282,39 @@ fn list_attempts_by_endpoint_or_message_filters(
         }
 
         if let Some(channel) = channel {
-            query = query.filter(Expr::cust_with_values("channels ?? ?", vec![channel]));
+            // sea_orm evaluates the '$1' relative to the # of params in `Expr::cust_with_values`,
+            // NOT relative to the total number of params in the final query like you might expect.
+            // As such, this won't break if more $N params are added in earler/later
+            // `.filter` calls.
+            query = query.filter(Expr::cust_with_values("channels @> $1", [channel.jsonb()]));
         }
     }
 
     query
 }
 
-/// Fetches a list of [`MessageAttemptOut`]s for a given endpoint ID
+/// List attempts by endpoint id
+#[aide_annotate(
+    op_id = "list_attempts_by_endpoint_api_v1_app__app_id__attempt_endpoint__endpoint_id___get"
+)]
 async fn list_attempts_by_endpoint(
     State(AppState { ref db, .. }): State<AppState>,
     ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<MessageAttemptId>>>,
     ValidatedQuery(ListAttemptsByEndpointQueryParameters {
         status,
         status_code_class,
-        event_types,
         channel,
         before,
         after,
     }): ValidatedQuery<ListAttemptsByEndpointQueryParameters>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    EventTypesQuery(event_types): EventTypesQuery,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<ListResponse<MessageAttemptOut>>> {
     let PaginationLimit(limit) = pagination.limit;
     // Confirm endpoint ID belongs to the given application
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endpoint_id)
             .one(db)
             .await
     )?
@@ -324,20 +330,12 @@ async fn list_attempts_by_endpoint(
 
     let iterator = iterator_from_before_or_after(pagination.iterator, before, after);
     let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
-    let query = apply_pagination(query, messageattempt::Column::Id, limit, iterator);
+    let query = apply_pagination_desc(query, messageattempt::Column::Id, limit, iterator);
 
-    let out = if is_prev {
-        ctx!(query.all(db).await)?
-            .into_iter()
-            .rev()
-            .map(Into::into)
-            .collect()
-    } else {
-        ctx!(query.all(db).await)?
-            .into_iter()
-            .map(Into::into)
-            .collect()
-    };
+    let out = ctx!(query.all(db).await)?
+        .into_iter()
+        .map(Into::into)
+        .collect();
 
     Ok(Json(MessageAttemptOut::list_response(
         out,
@@ -352,8 +350,6 @@ pub struct ListAttemptsByMsgQueryParameters {
     status: Option<MessageStatus>,
     status_code_class: Option<StatusCodeClass>,
     #[validate]
-    event_types: Option<EventTypeNameSet>,
-    #[validate]
     channel: Option<EventChannel>,
     #[validate]
     endpoint_id: Option<EndpointIdOrUid>,
@@ -361,36 +357,34 @@ pub struct ListAttemptsByMsgQueryParameters {
     after: Option<DateTime<Utc>>,
 }
 
-/// Fetches a list of [`MessageAttemptOut`]s for a given message ID
+/// List attempts by message id
+#[aide_annotate(op_id = "list_attempts_by_msg_api_v1_app__app_id__attempt_msg__msg_id___get")]
 async fn list_attempts_by_msg(
     State(AppState { ref db, .. }): State<AppState>,
     ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<MessageAttemptId>>>,
     ValidatedQuery(ListAttemptsByMsgQueryParameters {
         status,
         status_code_class,
-        event_types,
         channel,
         endpoint_id,
         before,
         after,
     }): ValidatedQuery<ListAttemptsByMsgQueryParameters>,
-    Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageId)>,
+    Path(ApplicationMsgPath { msg_id, .. }): Path<ApplicationMsgPath>,
+    EventTypesQuery(event_types): EventTypesQuery,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<ListResponse<MessageAttemptOut>>> {
     let PaginationLimit(limit) = pagination.limit;
     // Confirm message ID belongs to the given application
-    if ctx!(
-        message::Entity::secure_find_by_id(app.id.clone(), msg_id.clone())
+    let msg = ctx!(
+        message::Entity::secure_find_by_id_or_uid(app.id.clone(), msg_id)
             .one(db)
             .await
     )?
-    .is_none()
-    {
-        return Err(Error::http(HttpError::not_found(None, None)));
-    }
+    .ok_or_else(|| HttpError::not_found(None, None))?;
 
     let mut query = list_attempts_by_endpoint_or_message_filters(
-        messageattempt::Entity::secure_find_by_msg(msg_id),
+        messageattempt::Entity::secure_find_by_msg(msg.id),
         status,
         status_code_class,
         event_types,
@@ -413,19 +407,11 @@ async fn list_attempts_by_msg(
 
     let iterator = iterator_from_before_or_after(pagination.iterator, before, after);
     let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
-    let query = apply_pagination(query, messageattempt::Column::Id, limit, iterator);
-    let out = if is_prev {
-        ctx!(query.all(db).await)?
-            .into_iter()
-            .rev()
-            .map(Into::into)
-            .collect()
-    } else {
-        ctx!(query.all(db).await)?
-            .into_iter()
-            .map(Into::into)
-            .collect()
-    };
+    let query = apply_pagination_desc(query, messageattempt::Column::Id, limit, iterator);
+    let out = ctx!(query.all(db).await)?
+        .into_iter()
+        .map(Into::into)
+        .collect();
 
     Ok(Json(MessageAttemptOut::list_response(
         out,
@@ -463,10 +449,14 @@ impl MessageEndpointOut {
     }
 }
 
+/// `msg_id`: Use a message id or a message `eventId`
+#[aide_annotate(
+    op_id = "list_attempted_destinations_api_v1_app__app_id__msg__msg_id__endpoint__get"
+)]
 async fn list_attempted_destinations(
     State(AppState { ref db, .. }): State<AppState>,
     ValidatedQuery(mut pagination): ValidatedQuery<Pagination<EndpointId>>,
-    Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageIdOrUid)>,
+    Path(ApplicationMsgPath { msg_id, .. }): Path<ApplicationMsgPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<ListResponse<MessageEndpointOut>>> {
     let PaginationLimit(limit) = pagination.limit;
@@ -519,6 +509,16 @@ pub struct ListAttemptsForEndpointQueryParameters {
     pub after: Option<DateTime<Utc>>,
 }
 
+/// DEPRECATED: please use list_attempts with endpoint_id as a query parameter instead.
+///
+/// List the message attempts for a particular endpoint.
+///
+/// Returning the endpoint.
+///
+/// The `before` parameter lets you filter all items created before a certain date and is ignored if an iterator is passed.
+#[aide_annotate(
+    op_id = "list_attempts_for_endpoint_api_v1_app__app_id__msg__msg_id__endpoint__endpoint_id__attempt__get"
+)]
 async fn list_attempts_for_endpoint(
     state: State<AppState>,
     pagination: ValidatedQuery<Pagination<ReversibleIterator<MessageAttemptId>>>,
@@ -528,22 +528,26 @@ async fn list_attempts_for_endpoint(
         before,
         after,
     }): ValidatedQuery<ListAttemptsForEndpointQueryParameters>,
-    list_filter: MessageListFetchOptions,
-    Path((app_id, msg_id, endp_id)): Path<(ApplicationIdOrUid, MessageIdOrUid, EndpointIdOrUid)>,
+    event_types_query: EventTypesQuery,
+    Path(ApplicationMsgEndpointPath {
+        app_id,
+        msg_id,
+        endpoint_id,
+    }): Path<ApplicationMsgEndpointPath>,
     auth_app: permissions::Application,
 ) -> Result<Json<ListResponse<MessageAttemptOut>>> {
     list_messageattempts(
         state,
         pagination,
         ValidatedQuery(AttemptListFetchOptions {
-            endpoint_id: Some(endp_id),
+            endpoint_id: Some(endpoint_id),
             channel,
             status,
             before,
             after,
         }),
-        list_filter,
-        Path((app_id, msg_id)),
+        event_types_query,
+        Path(ApplicationMsgPath { app_id, msg_id }),
         auth_app,
     )
     .await
@@ -560,6 +564,10 @@ pub struct AttemptListFetchOptions {
     pub after: Option<DateTime<Utc>>,
 }
 
+/// Deprecated: Please use "List Attempts by Endpoint" and "List Attempts by Msg" instead.
+///
+/// `msg_id`: Use a message id or a message `eventId`
+#[aide_annotate(op_id = "list_attempts_api_v1_app__app_id__msg__msg_id__attempt__get")]
 async fn list_messageattempts(
     State(AppState { ref db, .. }): State<AppState>,
     ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<MessageAttemptId>>>,
@@ -570,8 +578,8 @@ async fn list_messageattempts(
         before,
         after,
     }): ValidatedQuery<AttemptListFetchOptions>,
-    list_filter: MessageListFetchOptions,
-    Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageIdOrUid)>,
+    EventTypesQuery(event_types): EventTypesQuery,
+    Path(ApplicationMsgPath { msg_id, .. }): Path<ApplicationMsgPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<ListResponse<MessageAttemptOut>>> {
     let PaginationLimit(limit) = pagination.limit;
@@ -599,43 +607,35 @@ async fn list_messageattempts(
     }
 
     if let Some(channel) = channel {
-        query = query.filter(Expr::cust_with_values("channels ?? ?", vec![channel]));
+        query = query.filter(Expr::cust_with_values("channels @> $1", [channel.jsonb()]));
     }
 
-    if let Some(EventTypeNameSet(event_types)) = list_filter.event_types {
+    if let Some(EventTypeNameSet(event_types)) = event_types {
         query = query.filter(message::Column::EventType.is_in(event_types));
     }
 
     let iterator = iterator_from_before_or_after(pagination.iterator, before, after);
     let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
-    let query = apply_pagination(query, messageattempt::Column::Id, limit, iterator);
-    let out = if is_prev {
-        ctx!(query.all(db).await)?
-            .into_iter()
-            .rev()
-            .map(Into::into)
-            .collect()
-    } else {
-        ctx!(query.all(db).await)?
-            .into_iter()
-            .map(Into::into)
-            .collect()
-    };
+    let query = apply_pagination_desc(query, messageattempt::Column::Id, limit, iterator);
+    let out = ctx!(query.all(db).await)?
+        .into_iter()
+        .map(Into::into)
+        .collect();
 
     Ok(Json(MessageAttemptOut::list_response(
         out,
         limit as usize,
-        false,
+        is_prev,
     )))
 }
 
+/// `msg_id`: Use a message id or a message `eventId`
+#[aide_annotate(op_id = "get_attempt_api_v1_app__app_id__msg__msg_id__attempt__attempt_id___get")]
 async fn get_messageattempt(
     State(AppState { ref db, .. }): State<AppState>,
-    Path((_app_id, msg_id, attempt_id)): Path<(
-        ApplicationIdOrUid,
-        MessageIdOrUid,
-        MessageAttemptId,
-    )>,
+    Path(ApplicationMsgAttemptPath {
+        msg_id, attempt_id, ..
+    }): Path<ApplicationMsgAttemptPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<MessageAttemptOut>> {
     let msg = ctx!(
@@ -655,13 +655,21 @@ async fn get_messageattempt(
     Ok(Json(attempt.into()))
 }
 
+/// Resend a message to the specified endpoint.
+#[aide_annotate(
+    op_id = "resend_webhook_api_v1_app__app_id__msg__msg_id__endpoint__endpoint_id__resend__post"
+)]
 async fn resend_webhook(
     State(AppState {
         ref db, queue_tx, ..
     }): State<AppState>,
-    Path((_app_id, msg_id, endp_id)): Path<(ApplicationIdOrUid, MessageIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationMsgEndpointPath {
+        msg_id,
+        endpoint_id,
+        ..
+    }): Path<ApplicationMsgEndpointPath>,
     permissions::Application { app }: permissions::Application,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<202, EmptyResponse>> {
     let msg = ctx!(
         message::Entity::secure_find_by_id_or_uid(app.id.clone(), msg_id)
             .one(db)
@@ -678,7 +686,7 @@ async fn resend_webhook(
     }
 
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endpoint_id)
             .one(db)
             .await
     )?
@@ -704,52 +712,100 @@ async fn resend_webhook(
             None,
         )
         .await?;
-    Ok((StatusCode::ACCEPTED, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
+}
+
+/// Deletes the given attempt's response body. Useful when an endpoint accidentally returned sensitive content.
+#[aide_annotate(
+    op_id = "expunge_attempt_content_api_v1_app__app_id__msg__msg_id__attempt__attempt_id__content__delete"
+)]
+async fn expunge_attempt_content(
+    State(AppState { ref db, .. }): State<AppState>,
+    Path(ApplicationMsgAttemptPath {
+        msg_id, attempt_id, ..
+    }): Path<ApplicationMsgAttemptPath>,
+    permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication,
+) -> Result<StatusCode> {
+    let msg = ctx!(
+        message::Entity::secure_find_by_id_or_uid(app.id, msg_id)
+            .one(db)
+            .await
+    )?
+    .ok_or_else(|| HttpError::not_found(None, Some("Message not found".to_string())))?;
+
+    let mut attempt = ctx!(
+        messageattempt::Entity::secure_find_by_msg(msg.id)
+            .filter(messageattempt::Column::Id.eq(attempt_id))
+            .one(db)
+            .await
+    )?
+    .ok_or_else(|| HttpError::not_found(None, Some("Message attempt not found".to_string())))?
+    .into_active_model();
+
+    attempt.response = sea_orm::Set("EXPUNGED".to_string());
+    ctx!(attempt.update(db).await)?;
+
+    Ok(StatusCode::NO_CONTENT)
 }
 
 pub fn router() -> ApiRouter<AppState> {
+    let tag = openapi_tag("Message Attempt");
     ApiRouter::new()
         // NOTE: [`list_messageattempts`] is deprecated
         .api_route_with(
             "/app/:app_id/msg/:msg_id/attempt/",
-            get(list_messageattempts),
-            openapi_tag("Message Attempt"),
+            get_with(list_messageattempts, list_messageattempts_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/msg/:msg_id/attempt/:attempt_id/",
-            get(get_messageattempt),
-            openapi_tag("Message Attempt"),
+            get_with(get_messageattempt, get_messageattempt_operation),
+            &tag,
+        )
+        .api_route_with(
+            "/app/:app_id/msg/:msg_id/attempt/:attempt_id/content/",
+            delete_with(expunge_attempt_content, expunge_attempt_content_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/msg/:msg_id/endpoint/",
-            get(list_attempted_destinations),
-            openapi_tag("Message Attempt"),
+            get_with(
+                list_attempted_destinations,
+                list_attempted_destinations_operation,
+            ),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/msg/:msg_id/endpoint/:endpoint_id/resend/",
-            post(resend_webhook),
-            openapi_tag("Message Attempt"),
+            post_with(resend_webhook, resend_webhook_operation),
+            &tag,
         )
         // NOTE: [`list_attempts_for_endpoint`] is deprecated
         .api_route_with(
             "/app/:app_id/msg/:msg_id/endpoint/:endpoint_id/attempt/",
-            get(list_attempts_for_endpoint),
-            openapi_tag("Message Attempt"),
+            get_with(
+                list_attempts_for_endpoint,
+                list_attempts_for_endpoint_operation,
+            ),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/msg/",
-            get(list_attempted_messages),
-            openapi_tag("Message Attempt"),
+            get_with(list_attempted_messages, list_attempted_messages_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/attempt/endpoint/:endpoint_id/",
-            get(list_attempts_by_endpoint),
-            openapi_tag("Message Attempt"),
+            get_with(
+                list_attempts_by_endpoint,
+                list_attempts_by_endpoint_operation,
+            ),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/attempt/msg/:msg_id/",
-            get(list_attempts_by_msg),
-            openapi_tag("Message Attempt"),
+            get_with(list_attempts_by_msg, list_attempts_by_msg_operation),
+            tag,
         )
 }
 
@@ -765,8 +821,6 @@ mod tests {
 
     const INVALID_CHANNEL: &str = "$$invalid-channel";
     const VALID_CHANNEL: &str = "valid-channel";
-    const INVALID_EVENT_TYPES: &[&str] = &["valid-event-type", "&&invalid-event-type"];
-    const VALID_EVENT_TYPES: &[&str] = &["valid-event-type", "another-valid-event-type"];
     const INVALID_ENDPOINT_ID: &str = "$$invalid-endpoint";
     const VALID_ENDPOINT_ID: &str = "ep_valid-endpoint";
 
@@ -783,10 +837,6 @@ mod tests {
 
     #[test]
     fn test_list_attempts_by_endpoint_query_parameters_validation() {
-        let q: ListAttemptsByEndpointQueryParameters =
-            serde_json::from_value(json!({ "event_types": INVALID_EVENT_TYPES })).unwrap();
-        assert!(q.validate().is_err());
-
         let q: ListAttemptsByEndpointQueryParameters =
             serde_json::from_value(json!({ "channel": INVALID_CHANNEL })).unwrap();
         assert!(q.validate().is_err());
@@ -794,10 +844,6 @@ mod tests {
 
     #[test]
     fn test_list_attempts_by_msg_query_parameters_validation() {
-        let q: ListAttemptsByMsgQueryParameters =
-            serde_json::from_value(json!({ "event_types": INVALID_EVENT_TYPES })).unwrap();
-        assert!(q.validate().is_err());
-
         let q: ListAttemptsByMsgQueryParameters =
             serde_json::from_value(json!({ "channel": INVALID_CHANNEL })).unwrap();
         assert!(q.validate().is_err());
@@ -808,7 +854,6 @@ mod tests {
 
         let q: ListAttemptsByMsgQueryParameters = serde_json::from_value(json!(
             {
-                "event_types": VALID_EVENT_TYPES,
                 "channel": VALID_CHANNEL,
                 "endpoint_id": VALID_ENDPOINT_ID
             }
diff --git a/server/svix-server/src/v1/endpoints/auth.rs b/server/svix-server/src/v1/endpoints/auth.rs
index 44693725e..b80b7bedb 100644
--- a/server/svix-server/src/v1/endpoints/auth.rs
+++ b/server/svix-server/src/v1/endpoints/auth.rs
@@ -1,7 +1,11 @@
-use aide::axum::{routing::post, ApiRouter};
+use aide::{
+    axum::{routing::post_with, ApiRouter},
+    transform::TransformOperation,
+};
 use axum::{extract::State, Json};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use svix_server_derive::aide_annotate;
 use validator::Validate;
 
 use crate::{
@@ -27,6 +31,8 @@ pub struct AppPortalAccessIn {
 
 pub type AppPortalAccessOut = DashboardAccessOut;
 
+/// Use this function to get magic links (and authentication codes) for connecting your users to the Consumer Application Portal.
+#[aide_annotate(op_id = "get_app_portal_access_api_v1_auth_app_portal_access__app_id___post")]
 async fn app_portal_access(
     State(AppState { cfg, .. }): State<AppState>,
     permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication,
@@ -54,6 +60,10 @@ async fn app_portal_access(
     Ok(Json(DashboardAccessOut { url, token }))
 }
 
+/// DEPRECATED: Please use `app-portal-access` instead.
+///
+/// Use this function to get magic links (and authentication codes) for connecting your users to the Consumer Application Portal.
+#[aide_annotate(op_id = "get_dashboard_access_api_v1_auth_dashboard_access__app_id___post")]
 async fn dashboard_access(
     state: State<AppState>,
     permissions: permissions::OrganizationWithApplication,
@@ -68,21 +78,34 @@ async fn dashboard_access(
     .await
 }
 
+const LOGOUT_DESCRIPTION: &str = r#"
+Logout an app token.
+
+Trying to log out other tokens will fail.
+"#;
+
+fn logout_operation(op: TransformOperation) -> TransformOperation {
+    op.id("logout_api_v1_auth_logout__post")
+        .summary("Logout")
+        .description(LOGOUT_DESCRIPTION)
+}
+
 pub fn router() -> ApiRouter<AppState> {
+    let tag = openapi_tag("Authentication");
     ApiRouter::new()
         .api_route_with(
             "/auth/dashboard-access/:app_id/",
-            post(dashboard_access),
-            openapi_tag("Authentication"),
+            post_with(dashboard_access, dashboard_access_operation),
+            &tag,
         )
         .api_route_with(
             "/auth/logout/",
-            post(api_not_implemented),
-            openapi_tag("Authentication"),
+            post_with(api_not_implemented, logout_operation),
+            &tag,
         )
         .api_route_with(
             "/auth/app-portal-access/:app_id/",
-            post(app_portal_access),
-            openapi_tag("Authentication"),
+            post_with(app_portal_access, app_portal_access_operation),
+            tag,
         )
 }
diff --git a/server/svix-server/src/v1/endpoints/endpoint/crud.rs b/server/svix-server/src/v1/endpoints/endpoint/crud.rs
index 2c5d6b679..74eb78053 100644
--- a/server/svix-server/src/v1/endpoints/endpoint/crud.rs
+++ b/server/svix-server/src/v1/endpoints/endpoint/crud.rs
@@ -4,9 +4,9 @@ use axum::{
     extract::{Path, State},
     Json,
 };
-use hyper::StatusCode;
-use sea_orm::{entity::prelude::*, ActiveValue::Set, QueryOrder, TransactionTrait};
+use sea_orm::{entity::prelude::*, ActiveValue::Set, TransactionTrait};
 use sea_orm::{ActiveModelTrait, DatabaseConnection, QuerySelect};
+use svix_server_derive::aide_annotate;
 use url::Url;
 
 use super::{EndpointIn, EndpointOut, EndpointPatch};
@@ -15,38 +15,40 @@ use crate::{
     core::{
         operational_webhooks::{EndpointEvent, OperationalWebhook, OperationalWebhookSender},
         permissions,
-        types::{
-            ApplicationIdOrUid, EndpointId, EndpointIdOrUid, EventTypeName, EventTypeNameSet,
-            OrganizationId,
-        },
+        types::{EndpointId, EventTypeName, EventTypeNameSet, OrganizationId},
     },
     ctx,
     db::models::{application, endpoint, endpointmetadata, eventtype},
-    error::{HttpError, Result, ValidationErrorItem},
+    error::{http_error_on_conflict, HttpError, Result, ValidationErrorItem},
     v1::utils::{
+        apply_pagination,
         patch::{patch_field_non_nullable, UnrequiredField, UnrequiredNullableField},
-        EmptyResponse, ListResponse, ModelIn, ModelOut, Pagination, PaginationLimit, ValidatedJson,
-        ValidatedQuery,
+        ApplicationEndpointPath, EmptyResponse, JsonStatus, JsonStatusUpsert, ListOrdering,
+        ListResponse, ModelIn, ModelOut, Pagination, PaginationLimit, ReversibleIterator,
+        ValidatedJson, ValidatedQuery,
     },
     AppState,
 };
 use hack::EventTypeNameResult;
 
+/// List the application's endpoints.
+#[aide_annotate(op_id = "list_endpoints_api_v1_app__app_id__endpoint__get")]
 pub(super) async fn list_endpoints(
     State(AppState { ref db, .. }): State<AppState>,
-    pagination: ValidatedQuery<Pagination<EndpointId>>,
+    ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<EndpointId>>>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<ListResponse<EndpointOut>>> {
     let PaginationLimit(limit) = pagination.limit;
-    let iterator = pagination.iterator.clone();
+    let iterator = pagination.iterator;
+    let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
 
-    let mut query = endpoint::Entity::secure_find(app.id)
-        .order_by_asc(endpoint::Column::Id)
-        .limit(limit + 1);
-
-    if let Some(iterator) = iterator {
-        query = query.filter(endpoint::Column::Id.gt(iterator))
-    }
+    let query = apply_pagination(
+        endpoint::Entity::secure_find(app.id),
+        endpoint::Column::Id,
+        limit,
+        iterator,
+        pagination.order.unwrap_or(ListOrdering::Descending),
+    );
 
     let results = ctx!(
         query
@@ -61,9 +63,10 @@ pub(super) async fn list_endpoints(
     })
     .collect();
 
-    Ok(Json(EndpointOut::list_response_no_prev(
+    Ok(Json(EndpointOut::list_response(
         results,
         limit as usize,
+        is_prev,
     )))
 }
 
@@ -83,7 +86,7 @@ async fn create_endp_from_data(
 
     let (endp, metadata) = {
         let txn = ctx!(db.begin().await)?;
-        let endp = ctx!(endp.insert(&txn).await)?;
+        let endp = ctx!(endp.insert(&txn).await.map_err(http_error_on_conflict))?;
         let metadata = ctx!(metadata.upsert_or_delete(&txn).await)?;
         ctx!(txn.commit().await)?;
         (endp, metadata)
@@ -99,6 +102,10 @@ async fn create_endp_from_data(
     Ok((endp, metadata))
 }
 
+/// Create a new endpoint for the application.
+///
+/// When `secret` is `null` the secret is automatically generated (recommended)
+#[aide_annotate(op_id = "create_endpoint_api_v1_app__app_id__endpoint__post")]
 pub(super) async fn create_endpoint(
     State(AppState {
         ref db,
@@ -108,7 +115,7 @@ pub(super) async fn create_endpoint(
     }): State<AppState>,
     permissions::Application { app }: permissions::Application,
     ValidatedJson(data): ValidatedJson<EndpointIn>,
-) -> Result<(StatusCode, Json<EndpointOut>)> {
+) -> Result<JsonStatus<201, EndpointOut>> {
     if let Some(ref event_types_ids) = data.event_types_ids {
         validate_event_types(db, event_types_ids, &app.org_id).await?;
     }
@@ -116,16 +123,18 @@ pub(super) async fn create_endpoint(
 
     let (endp, metadata) = ctx!(create_endp_from_data(db, cfg, &op_webhooks, app, data).await)?;
 
-    Ok((StatusCode::CREATED, Json((endp, metadata.data).into())))
+    Ok(JsonStatus((endp, metadata.data).into()))
 }
 
+/// Get an endpoint.
+#[aide_annotate(op_id = "get_endpoint_api_v1_app__app_id__endpoint__endpoint_id___get")]
 pub(super) async fn get_endpoint(
     State(AppState { ref db, .. }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<EndpointOut>> {
     let (endp, metadata) = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id, endpoint_id)
             .find_also_related(endpointmetadata::Entity)
             .one(db)
             .await
@@ -146,7 +155,7 @@ async fn update_endp_from_data(
 ) -> Result<(endpoint::Model, endpointmetadata::Model)> {
     let (endp, metadata) = {
         let txn = ctx!(db.begin().await)?;
-        let endp = ctx!(endp.update(&txn).await)?;
+        let endp = ctx!(endp.update(&txn).await.map_err(http_error_on_conflict))?;
         let metadata = ctx!(metadata.upsert_or_delete(&txn).await)?;
         ctx!(txn.commit().await)?;
         (endp, metadata)
@@ -163,6 +172,8 @@ async fn update_endp_from_data(
     Ok((endp, metadata))
 }
 
+/// Update an endpoint.
+#[aide_annotate(op_id = "update_endpoint_api_v1_app__app_id__endpoint__endpoint_id___put")]
 pub(super) async fn update_endpoint(
     State(AppState {
         ref db,
@@ -170,30 +181,32 @@ pub(super) async fn update_endpoint(
         ref op_webhooks,
         ..
     }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
     ValidatedJson(mut data): ValidatedJson<EndpointIn>,
-) -> Result<(StatusCode, Json<EndpointOut>)> {
+) -> Result<JsonStatusUpsert<EndpointOut>> {
     if let Some(ref event_types_ids) = data.event_types_ids {
         validate_event_types(db, event_types_ids, &app.org_id).await?;
     }
     validate_endpoint_url(&data.url, cfg.endpoint_https_only)?;
 
     let models =
-        ctx!(endpoint::ActiveModel::fetch_with_metadata(db, app.id.clone(), endp_id).await)?;
+        ctx!(endpoint::ActiveModel::fetch_with_metadata(db, app.id.clone(), endpoint_id).await)?;
 
     if let Some((mut endp, mut metadata)) = models {
         metadata.data = Set(mem::take(&mut data.metadata));
         data.update_model(&mut endp);
         let (endp, metadata) =
             ctx!(update_endp_from_data(db, op_webhooks, app, endp, metadata).await)?;
-        Ok((StatusCode::OK, Json((endp, metadata.data).into())))
+        Ok(JsonStatusUpsert::Updated((endp, metadata.data).into()))
     } else {
         let (endp, metadata) = ctx!(create_endp_from_data(db, cfg, op_webhooks, app, data).await)?;
-        Ok((StatusCode::CREATED, Json((endp, metadata.data).into())))
+        Ok(JsonStatusUpsert::Created((endp, metadata.data).into()))
     }
 }
 
+/// Partially update an endpoint.
+#[aide_annotate]
 pub(super) async fn patch_endpoint(
     State(AppState {
         ref db,
@@ -201,7 +214,7 @@ pub(super) async fn patch_endpoint(
         ref op_webhooks,
         ..
     }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
     ValidatedJson(data): ValidatedJson<EndpointPatch>,
 ) -> Result<Json<EndpointOut>> {
@@ -213,7 +226,7 @@ pub(super) async fn patch_endpoint(
     }
 
     let (mut endp, mut metadata) =
-        ctx!(endpoint::ActiveModel::fetch_with_metadata(db, app.id.clone(), endp_id).await)?
+        ctx!(endpoint::ActiveModel::fetch_with_metadata(db, app.id.clone(), endpoint_id).await)?
             .ok_or_else(|| HttpError::not_found(None, None))?;
 
     let mut patch_data = data; // need to alias so we can use data for `patch_field_non_nullable!`
@@ -226,17 +239,19 @@ pub(super) async fn patch_endpoint(
     Ok(Json((endp, metadata.data).into()))
 }
 
+/// Delete an endpoint.
+#[aide_annotate(op_id = "delete_endpoint_api_v1_app__app_id__endpoint__endpoint_id___delete")]
 pub(super) async fn delete_endpoint(
     State(AppState {
         ref db,
         ref op_webhooks,
         ..
     }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<204, EmptyResponse>> {
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endpoint_id)
             .one(db)
             .await
     )?
@@ -263,7 +278,7 @@ pub(super) async fn delete_endpoint(
         )
         .await?;
 
-    Ok((StatusCode::NO_CONTENT, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
 }
 
 /// This module is here so that our Result override doesn't conflict
@@ -303,9 +318,14 @@ async fn validate_event_types(
     if missing.is_empty() {
         Ok(())
     } else {
+        let missing = missing
+            .into_iter()
+            .map(|x| &(x.0[..]))
+            .collect::<Vec<&str>>()
+            .join(", ");
         Err(HttpError::unprocessable_entity(vec![ValidationErrorItem {
-            loc: vec!["body".to_owned(), "event_types_ids".to_owned()],
-            msg: format!("The following type names don't exist: {missing:?}"),
+            loc: vec!["body".to_owned(), "filterTypes".to_owned()],
+            msg: format!("The following event types don't exist: {missing}"),
             ty: "value_error".to_owned(),
         }])
         .into())
diff --git a/server/svix-server/src/v1/endpoints/endpoint/headers.rs b/server/svix-server/src/v1/endpoints/endpoint/headers.rs
index 887131718..314601951 100644
--- a/server/svix-server/src/v1/endpoints/endpoint/headers.rs
+++ b/server/svix-server/src/v1/endpoints/endpoint/headers.rs
@@ -2,29 +2,30 @@ use axum::{
     extract::{Path, State},
     Json,
 };
-use hyper::StatusCode;
 use sea_orm::ActiveModelTrait;
+use svix_server_derive::aide_annotate;
 
 use super::{EndpointHeadersIn, EndpointHeadersOut, EndpointHeadersPatchIn};
 use crate::{
-    core::{
-        permissions,
-        types::{ApplicationIdOrUid, EndpointIdOrUid},
-    },
+    core::permissions,
     ctx,
     db::models::endpoint,
     error::{HttpError, Result},
-    v1::utils::{EmptyResponse, ModelIn, ValidatedJson},
+    v1::utils::{ApplicationEndpointPath, EmptyResponse, JsonStatus, ModelIn, ValidatedJson},
     AppState,
 };
 
+/// Get the additional headers to be sent with the webhook
+#[aide_annotate(
+    op_id = "get_endpoint_headers_api_v1_app__app_id__endpoint__endpoint_id__headers__get"
+)]
 pub(super) async fn get_endpoint_headers(
     State(AppState { ref db, .. }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<EndpointHeadersOut>> {
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id, endpoint_id)
             .one(db)
             .await
     )?
@@ -36,14 +37,18 @@ pub(super) async fn get_endpoint_headers(
     }
 }
 
+/// Set the additional headers to be sent with the webhook
+#[aide_annotate(
+    op_id = "update_endpoint_headers_api_v1_app__app_id__endpoint__endpoint_id__headers__put"
+)]
 pub(super) async fn update_endpoint_headers(
     State(AppState { ref db, .. }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
     ValidatedJson(data): ValidatedJson<EndpointHeadersIn>,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<204, EmptyResponse>> {
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endpoint_id)
             .one(db)
             .await
     )?
@@ -53,17 +58,21 @@ pub(super) async fn update_endpoint_headers(
     data.update_model(&mut endp);
     ctx!(endp.update(db).await)?;
 
-    Ok((StatusCode::NO_CONTENT, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
 }
 
+/// Partially set the additional headers to be sent with the webhook
+#[aide_annotate(
+    op_id = "patch_endpoint_headers_api_v1_app__app_id__endpoint__endpoint_id__headers__patch"
+)]
 pub(super) async fn patch_endpoint_headers(
     State(AppState { ref db, .. }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
     ValidatedJson(data): ValidatedJson<EndpointHeadersPatchIn>,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<204, EmptyResponse>> {
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endpoint_id)
             .one(db)
             .await
     )?
@@ -73,7 +82,7 @@ pub(super) async fn patch_endpoint_headers(
     data.update_model(&mut endp);
     ctx!(endp.update(db).await)?;
 
-    Ok((StatusCode::NO_CONTENT, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
 }
 
 #[cfg(test)]
diff --git a/server/svix-server/src/v1/endpoints/endpoint/mod.rs b/server/svix-server/src/v1/endpoints/endpoint/mod.rs
index 965b5a553..830b3c8a5 100644
--- a/server/svix-server/src/v1/endpoints/endpoint/mod.rs
+++ b/server/svix-server/src/v1/endpoints/endpoint/mod.rs
@@ -11,28 +11,27 @@ use crate::{
         cryptography::Encryption,
         permissions,
         types::{
-            metadata::Metadata, ApplicationIdOrUid, BaseId, EndpointId, EndpointIdOrUid,
-            EndpointSecretInternal, EndpointUid, EventChannelSet, EventTypeNameSet,
-            MessageEndpointId, MessageStatus,
+            metadata::Metadata, BaseId, EndpointId, EndpointSecretInternal, EndpointUid,
+            EventChannelSet, EventTypeNameSet, MessageEndpointId, MessageStatus,
         },
     },
     ctx,
     db::models::messagedestination,
     error::{self, HttpError},
     v1::utils::{
-        api_not_implemented, openapi_tag,
+        api_not_implemented, openapi_desc, openapi_tag,
         patch::{
             patch_field_non_nullable, patch_field_nullable, UnrequiredField,
             UnrequiredNullableField,
         },
         validate_no_control_characters, validate_no_control_characters_unrequired,
-        validation_error, ModelIn,
+        validation_error, ApplicationEndpointPath, ModelIn,
     },
     AppState,
 };
 
 use aide::axum::{
-    routing::{get, post},
+    routing::{get_with, post_with},
     ApiRouter,
 };
 use axum::{
@@ -46,7 +45,7 @@ use serde::{Deserialize, Serialize};
 use std::{collections::HashMap, collections::HashSet};
 use url::Url;
 
-use svix_server_derive::{ModelIn, ModelOut};
+use svix_server_derive::{aide_annotate, ModelIn, ModelOut};
 use validator::{Validate, ValidationError};
 
 use crate::core::types::{EndpointHeaders, EndpointHeadersPatch, EndpointSecret};
@@ -375,7 +374,7 @@ impl From<(endpoint::Model, Metadata)> for EndpointOut {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Validate, Deserialize, JsonSchema)]
+#[derive(Default, Clone, Debug, PartialEq, Eq, Validate, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "camelCase")]
 pub struct EndpointSecretRotateIn {
     #[validate]
@@ -524,16 +523,18 @@ pub struct EndpointStatsQueryOut {
     count: i64,
 }
 
+/// Get basic statistics for the endpoint.
+#[aide_annotate(op_id = "get_endpoint_stats_api_v1_app__app_id__endpoint__endpoint_id__stats__get")]
 async fn endpoint_stats(
     State(AppState { ref db, .. }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     Query(range): Query<EndpointStatsRange>,
     permissions::Application { app }: permissions::Application,
 ) -> error::Result<Json<EndpointStatsOut>> {
     let (since, until) = range.validate_unwrap_or_default()?;
 
     let endpoint = ctx!(
-        crate::db::models::endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id)
+        crate::db::models::endpoint::Entity::secure_find_by_id_or_uid(app.id, endpoint_id)
             .one(db)
             .await
     )?
@@ -565,52 +566,74 @@ async fn endpoint_stats(
     }))
 }
 
+const SEND_EXAMPLE_DESCRIPTION: &str = "Send an example message for event";
+
 pub fn router() -> ApiRouter<AppState> {
+    let tag = openapi_tag("Endpoint");
     ApiRouter::new()
         .api_route_with(
             "/app/:app_id/endpoint/",
-            post(crud::create_endpoint).get(crud::list_endpoints),
-            openapi_tag("Endpoint"),
+            post_with(crud::create_endpoint, crud::create_endpoint_operation)
+                .get_with(crud::list_endpoints, crud::list_endpoints_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/",
-            get(crud::get_endpoint)
-                .put(crud::update_endpoint)
-                .patch(crud::patch_endpoint)
-                .delete(crud::delete_endpoint),
-            openapi_tag("Endpoint"),
+            get_with(crud::get_endpoint, crud::get_endpoint_operation)
+                .put_with(crud::update_endpoint, crud::update_endpoint_operation)
+                .patch_with(crud::patch_endpoint, crud::patch_endpoint_operation)
+                .delete_with(crud::delete_endpoint, crud::delete_endpoint_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/secret/",
-            get(secrets::get_endpoint_secret),
-            openapi_tag("Endpoint"),
+            get_with(
+                secrets::get_endpoint_secret,
+                secrets::get_endpoint_secret_operation,
+            ),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/secret/rotate/",
-            post(secrets::rotate_endpoint_secret),
-            openapi_tag("Endpoint"),
+            post_with(
+                secrets::rotate_endpoint_secret,
+                secrets::rotate_endpoint_secret_operation,
+            ),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/stats/",
-            get(endpoint_stats),
-            openapi_tag("Endpoint"),
+            get_with(endpoint_stats, endpoint_stats_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/send-example/",
-            post(api_not_implemented),
-            openapi_tag("Endpoint"),
+            post_with(api_not_implemented, openapi_desc(SEND_EXAMPLE_DESCRIPTION)),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/recover/",
-            post(recovery::recover_failed_webhooks),
-            openapi_tag("Endpoint"),
+            post_with(
+                recovery::recover_failed_webhooks,
+                recovery::recover_failed_webhooks_operation,
+            ),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/endpoint/:endpoint_id/headers/",
-            get(headers::get_endpoint_headers)
-                .patch(headers::patch_endpoint_headers)
-                .put(headers::update_endpoint_headers),
-            openapi_tag("Endpoint"),
+            get_with(
+                headers::get_endpoint_headers,
+                headers::get_endpoint_headers_operation,
+            )
+            .patch_with(
+                headers::patch_endpoint_headers,
+                headers::patch_endpoint_headers_operation,
+            )
+            .put_with(
+                headers::update_endpoint_headers,
+                headers::update_endpoint_headers_operation,
+            ),
+            tag,
         )
 }
 
diff --git a/server/svix-server/src/v1/endpoints/endpoint/recovery.rs b/server/svix-server/src/v1/endpoints/endpoint/recovery.rs
index 22ed38b2e..42483ee1c 100644
--- a/server/svix-server/src/v1/endpoints/endpoint/recovery.rs
+++ b/server/svix-server/src/v1/endpoints/endpoint/recovery.rs
@@ -1,26 +1,20 @@
-use axum::{
-    extract::{Path, State},
-    Json,
-};
+use axum::extract::{Path, State};
 use chrono::{DateTime, Utc};
-use hyper::StatusCode;
 use sea_orm::{entity::prelude::*, QueryOrder};
 use sea_orm::{DatabaseConnection, QuerySelect};
+use svix_server_derive::aide_annotate;
 
 use super::RecoverIn;
 use crate::{
     core::{
         permissions,
-        types::{
-            ApplicationIdOrUid, BaseId, EndpointIdOrUid, MessageAttemptTriggerType,
-            MessageEndpointId, MessageStatus,
-        },
+        types::{BaseId, MessageAttemptTriggerType, MessageEndpointId, MessageStatus},
     },
     ctx,
     db::models::{application, endpoint, messagedestination},
     error::{HttpError, Result, ValidationErrorItem},
     queue::{MessageTask, TaskQueueProducer},
-    v1::utils::{EmptyResponse, ValidatedJson},
+    v1::utils::{ApplicationEndpointPath, EmptyResponse, JsonStatus, ValidatedJson},
     AppState,
 };
 
@@ -74,14 +68,18 @@ async fn bulk_recover_failed_messages(
     Ok(())
 }
 
+/// Resend all failed messages since a given time.
+#[aide_annotate(
+    op_id = "recover_failed_webhooks_api_v1_app__app_id__endpoint__endpoint_id__recover__post"
+)]
 pub(super) async fn recover_failed_webhooks(
     State(AppState {
         ref db, queue_tx, ..
     }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
     ValidatedJson(data): ValidatedJson<RecoverIn>,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<202, EmptyResponse>> {
     // Add five minutes so that people can easily just do `now() - two_weeks` without having to worry about clock sync
     let timeframe = chrono::Duration::days(14);
     let timeframe = timeframe + chrono::Duration::minutes(5);
@@ -96,7 +94,7 @@ pub(super) async fn recover_failed_webhooks(
     }
 
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id.clone(), endpoint_id)
             .one(db)
             .await
     )?
@@ -108,5 +106,5 @@ pub(super) async fn recover_failed_webhooks(
         async move { bulk_recover_failed_messages(db, queue_tx, app, endp, data.since).await },
     );
 
-    Ok((StatusCode::ACCEPTED, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
 }
diff --git a/server/svix-server/src/v1/endpoints/endpoint/secrets.rs b/server/svix-server/src/v1/endpoints/endpoint/secrets.rs
index 031244102..70f30b480 100644
--- a/server/svix-server/src/v1/endpoints/endpoint/secrets.rs
+++ b/server/svix-server/src/v1/endpoints/endpoint/secrets.rs
@@ -3,26 +3,24 @@ use axum::{
     Json,
 };
 use chrono::{Duration, Utc};
-use hyper::StatusCode;
 use sea_orm::ActiveModelTrait;
 use sea_orm::ActiveValue::Set;
 use std::iter;
+use svix_server_derive::aide_annotate;
 
 use super::{EndpointSecretOut, EndpointSecretRotateIn};
 use crate::{
     cfg::DefaultSignatureType,
     core::{
         cryptography::Encryption,
+        operational_webhooks::{EndpointEvent, OperationalWebhook},
         permissions,
-        types::{
-            ApplicationIdOrUid, EndpointIdOrUid, EndpointSecretInternal, ExpiringSigningKey,
-            ExpiringSigningKeys,
-        },
+        types::{EndpointSecretInternal, ExpiringSigningKey, ExpiringSigningKeys},
     },
     ctx,
     db::models::endpoint,
     error::{HttpError, Result},
-    v1::utils::{EmptyResponse, ValidatedJson},
+    v1::utils::{ApplicationEndpointPath, EmptyResponse, JsonStatus, ValidatedJson},
     AppState,
 };
 
@@ -33,13 +31,20 @@ pub(super) fn generate_secret(
     EndpointSecretInternal::generate(encryption, sig_type.into())
 }
 
+/// Get the endpoint's signing secret.
+///
+/// This is used to verify the authenticity of the webhook.
+/// For more information please refer to [the consuming webhooks docs](https://docs.svix.com/consuming-webhooks/).
+#[aide_annotate(
+    op_id = "get_endpoint_secret_api_v1_app__app_id__endpoint__endpoint_id__secret__get"
+)]
 pub(super) async fn get_endpoint_secret(
     State(AppState { ref db, cfg, .. }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<EndpointSecretOut>> {
     let endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id, endpoint_id)
             .one(db)
             .await
     )?
@@ -49,14 +54,23 @@ pub(super) async fn get_endpoint_secret(
     }))
 }
 
+/// Rotates the endpoint's signing secret.  The previous secret will be valid for the next 24 hours.
+#[aide_annotate(
+    op_id = "rotate_endpoint_secret_api_v1_app__app_id__endpoint__endpoint_id__secret_rotate__post"
+)]
 pub(super) async fn rotate_endpoint_secret(
-    State(AppState { ref db, cfg, .. }): State<AppState>,
-    Path((_app_id, endp_id)): Path<(ApplicationIdOrUid, EndpointIdOrUid)>,
+    State(AppState {
+        ref db,
+        cfg,
+        ref op_webhooks,
+        ..
+    }): State<AppState>,
+    Path(ApplicationEndpointPath { endpoint_id, .. }): Path<ApplicationEndpointPath>,
     permissions::Application { app }: permissions::Application,
     ValidatedJson(data): ValidatedJson<EndpointSecretRotateIn>,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<204, EmptyResponse>> {
     let mut endp = ctx!(
-        endpoint::Entity::secure_find_by_id_or_uid(app.id, endp_id)
+        endpoint::Entity::secure_find_by_id_or_uid(app.id, endpoint_id)
             .one(db)
             .await
     )?
@@ -102,7 +116,14 @@ pub(super) async fn rotate_endpoint_secret(
         ))),
         ..endp.into()
     };
-    ctx!(endp.update(db).await)?;
+    let endp = ctx!(endp.update(db).await)?;
+
+    op_webhooks
+        .send_operational_webhook(
+            &app.org_id,
+            OperationalWebhook::EndpointUpdated(EndpointEvent::new(app.uid.as_ref(), &endp)),
+        )
+        .await?;
 
-    Ok((StatusCode::NO_CONTENT, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
 }
diff --git a/server/svix-server/src/v1/endpoints/event_type.rs b/server/svix-server/src/v1/endpoints/event_type.rs
index 0a9e27738..e1cf50839 100644
--- a/server/svix-server/src/v1/endpoints/event_type.rs
+++ b/server/svix-server/src/v1/endpoints/event_type.rs
@@ -8,21 +8,21 @@ use crate::{
     },
     ctx,
     db::models::eventtype,
-    error::{HttpError, Result},
+    error::{http_error_on_conflict, HttpError, Result},
     v1::utils::{
-        api_not_implemented, openapi_tag,
+        api_not_implemented, apply_pagination, openapi_desc, openapi_tag,
         patch::{
             patch_field_non_nullable, patch_field_nullable, UnrequiredField,
             UnrequiredNullableField,
         },
         validate_no_control_characters, validate_no_control_characters_unrequired, EmptyResponse,
-        ListResponse, ModelIn, ModelOut, Pagination, PaginationLimit, ValidatedJson,
-        ValidatedQuery,
+        EventTypeNamePath, JsonStatus, JsonStatusUpsert, ListOrdering, ListResponse, ModelIn,
+        ModelOut, Pagination, PaginationLimit, ReversibleIterator, ValidatedJson, ValidatedQuery,
     },
     AppState,
 };
 use aide::axum::{
-    routing::{get, post},
+    routing::{get, get_with, post_with},
     ApiRouter,
 };
 use axum::{
@@ -30,12 +30,10 @@ use axum::{
     Json,
 };
 use chrono::{DateTime, Utc};
-use hyper::StatusCode;
 use schemars::JsonSchema;
-use sea_orm::{entity::prelude::*, ActiveValue::Set, QueryOrder};
-use sea_orm::{ActiveModelTrait, QuerySelect};
+use sea_orm::{entity::prelude::*, ActiveModelTrait, ActiveValue::Set};
 use serde::{Deserialize, Serialize};
-use svix_server_derive::{ModelIn, ModelOut};
+use svix_server_derive::{aide_annotate, ModelIn, ModelOut};
 use validator::Validate;
 
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Validate, ModelIn, JsonSchema)]
@@ -189,9 +187,11 @@ pub struct ListFetchOptions {
     pub with_content: bool,
 }
 
+/// Return the list of event types.
+#[aide_annotate(op_id = "list_event_types_api_v1_event_type__get")]
 async fn list_event_types(
     State(AppState { ref db, .. }): State<AppState>,
-    pagination: ValidatedQuery<Pagination<EventTypeName>>,
+    ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<EventTypeName>>>,
     fetch_options: ValidatedQuery<ListFetchOptions>,
     permissions::ReadAll {
         org_id,
@@ -200,25 +200,28 @@ async fn list_event_types(
     }: permissions::ReadAll,
 ) -> Result<Json<ListResponse<EventTypeOut>>> {
     let PaginationLimit(limit) = pagination.limit;
-    let iterator = pagination.iterator.clone();
+    let iterator = pagination.iterator;
+    let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
 
-    let mut query = eventtype::Entity::secure_find(org_id)
-        .order_by_asc(eventtype::Column::Name)
-        .limit(limit + 1);
+    let mut query = eventtype::Entity::secure_find(org_id);
 
     if !fetch_options.include_archived {
         query = query.filter(eventtype::Column::Deleted.eq(false));
     }
 
-    if let Some(iterator) = iterator {
-        query = query.filter(eventtype::Column::Name.gt(iterator));
-    }
-
     if let permissions::AllowedFeatureFlags::Some(flags) = feature_flags {
         query = eventtype::Entity::filter_feature_flags(query, flags);
     }
 
-    Ok(Json(EventTypeOut::list_response_no_prev(
+    let query = apply_pagination(
+        query,
+        eventtype::Column::Name,
+        limit,
+        iterator,
+        ListOrdering::Ascending,
+    );
+
+    Ok(Json(EventTypeOut::list_response(
         ctx!(query.all(db).await)?
             .into_iter()
             .map(|x| {
@@ -230,14 +233,21 @@ async fn list_event_types(
             })
             .collect(),
         limit as usize,
+        is_prev,
     )))
 }
 
+/// Create new or unarchive existing event type.
+///
+/// Unarchiving an event type will allow endpoints to filter on it and messages to be sent with it.
+/// Endpoints filtering on the event type before archival will continue to filter on it.
+/// This operation does not preserve the description and schemas.
+#[aide_annotate(op_id = "create_event_type_api_v1_event_type__post")]
 async fn create_event_type(
     State(AppState { ref db, .. }): State<AppState>,
     permissions::Organization { org_id }: permissions::Organization,
     ValidatedJson(data): ValidatedJson<EventTypeIn>,
-) -> Result<(StatusCode, Json<EventTypeOut>)> {
+) -> Result<JsonStatus<201, EventTypeOut>> {
     let evtype = ctx!(
         eventtype::Entity::secure_find_by_name(org_id.clone(), data.name.to_owned())
             .one(db)
@@ -263,22 +273,24 @@ async fn create_event_type(
                 org_id: Set(org_id),
                 ..data.into()
             };
-            ctx!(evtype.insert(db).await)?
+            ctx!(evtype.insert(db).await.map_err(http_error_on_conflict))?
         }
     };
-    Ok((StatusCode::CREATED, Json(ret.into())))
+    Ok(JsonStatus(ret.into()))
 }
 
+/// Get an event type.
+#[aide_annotate(op_id = "get_event_type_api_v1_event_type__event_type_name___get")]
 async fn get_event_type(
     State(AppState { ref db, .. }): State<AppState>,
-    Path(evtype_name): Path<EventTypeName>,
+    Path(EventTypeNamePath { event_type_name }): Path<EventTypeNamePath>,
     permissions::ReadAll {
         org_id,
         feature_flags,
         ..
     }: permissions::ReadAll,
 ) -> Result<Json<EventTypeOut>> {
-    let mut query = eventtype::Entity::secure_find_by_name(org_id, evtype_name);
+    let mut query = eventtype::Entity::secure_find_by_name(org_id, event_type_name);
     if let permissions::AllowedFeatureFlags::Some(flags) = feature_flags {
         query = eventtype::Entity::filter_feature_flags(query, flags);
     }
@@ -287,14 +299,16 @@ async fn get_event_type(
     Ok(Json(evtype.into()))
 }
 
+/// Update an event type.
+#[aide_annotate(op_id = "update_event_type_api_v1_event_type__event_type_name___put")]
 async fn update_event_type(
     State(AppState { ref db, .. }): State<AppState>,
-    Path(evtype_name): Path<EventTypeName>,
+    Path(EventTypeNamePath { event_type_name }): Path<EventTypeNamePath>,
     permissions::Organization { org_id }: permissions::Organization,
     ValidatedJson(data): ValidatedJson<EventTypeUpdate>,
-) -> Result<(StatusCode, Json<EventTypeOut>)> {
+) -> Result<JsonStatusUpsert<EventTypeOut>> {
     let evtype = ctx!(
-        eventtype::Entity::secure_find_by_name(org_id.clone(), evtype_name.clone())
+        eventtype::Entity::secure_find_by_name(org_id.clone(), event_type_name.clone())
             .one(db)
             .await
     )?;
@@ -303,34 +317,35 @@ async fn update_event_type(
         Some(evtype) => {
             let mut evtype: eventtype::ActiveModel = evtype.into();
             data.update_model(&mut evtype);
-            let ret = ctx!(evtype.update(db).await)?;
+            let ret = ctx!(evtype.update(db).await.map_err(http_error_on_conflict))?;
 
-            Ok((StatusCode::OK, Json(ret.into())))
+            Ok(JsonStatusUpsert::Updated(ret.into()))
         }
         None => {
-            let ret = ctx!(
-                eventtype::ActiveModel {
-                    org_id: Set(org_id),
-                    name: Set(evtype_name),
-                    ..data.into()
-                }
-                .insert(db)
-                .await
-            )?;
+            let ret = ctx!(eventtype::ActiveModel {
+                org_id: Set(org_id),
+                name: Set(event_type_name),
+                ..data.into()
+            }
+            .insert(db)
+            .await
+            .map_err(http_error_on_conflict))?;
 
-            Ok((StatusCode::CREATED, Json(ret.into())))
+            Ok(JsonStatusUpsert::Created(ret.into()))
         }
     }
 }
 
+/// Partially update an event type.
+#[aide_annotate]
 async fn patch_event_type(
     State(AppState { ref db, .. }): State<AppState>,
-    Path(evtype_name): Path<EventTypeName>,
+    Path(EventTypeNamePath { event_type_name }): Path<EventTypeNamePath>,
     permissions::Organization { org_id }: permissions::Organization,
     ValidatedJson(data): ValidatedJson<EventTypePatch>,
 ) -> Result<Json<EventTypeOut>> {
     let evtype = ctx!(
-        eventtype::Entity::secure_find_by_name(org_id, evtype_name)
+        eventtype::Entity::secure_find_by_name(org_id, event_type_name)
             .one(db)
             .await
     )?
@@ -339,17 +354,24 @@ async fn patch_event_type(
     let mut evtype: eventtype::ActiveModel = evtype.into();
     data.update_model(&mut evtype);
 
-    let ret = ctx!(evtype.update(db).await)?;
+    let ret = ctx!(evtype.update(db).await.map_err(http_error_on_conflict))?;
     Ok(Json(ret.into()))
 }
 
+/// Archive an event type.
+///
+/// Endpoints already configured to filter on an event type will continue to do so after archival.
+/// However, new messages can not be sent with it and endpoints can not filter on it.
+/// An event type can be unarchived with the
+/// [create operation](#operation/create_event_type_api_v1_event_type__post).
+#[aide_annotate(op_id = "delete_event_type_api_v1_event_type__event_type_name___delete")]
 async fn delete_event_type(
     State(AppState { ref db, .. }): State<AppState>,
-    Path(evtype_name): Path<EventTypeName>,
+    Path(EventTypeNamePath { event_type_name }): Path<EventTypeNamePath>,
     permissions::Organization { org_id }: permissions::Organization,
-) -> Result<(StatusCode, Json<EmptyResponse>)> {
+) -> Result<JsonStatus<204, EmptyResponse>> {
     let evtype = ctx!(
-        eventtype::Entity::secure_find_by_name(org_id, evtype_name)
+        eventtype::Entity::secure_find_by_name(org_id, event_type_name)
             .one(db)
             .await
     )?
@@ -358,9 +380,12 @@ async fn delete_event_type(
     let mut evtype: eventtype::ActiveModel = evtype.into();
     evtype.deleted = Set(true);
     ctx!(evtype.update(db).await)?;
-    Ok((StatusCode::NO_CONTENT, Json(EmptyResponse {})))
+    Ok(JsonStatus(EmptyResponse {}))
 }
 
+const GENERATE_SCHEMA_EXAMPLE_DESCRIPTION: &str =
+    "Generates a fake example from the given JSONSchema";
+
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Validate, JsonSchema)]
 #[serde(rename_all = "camelCase")]
 pub struct RetryScheduleInOut {
@@ -412,19 +437,21 @@ async fn update_retry_schedule(
 }
 
 pub fn router() -> ApiRouter<AppState> {
+    let tag = openapi_tag("Event Type");
     ApiRouter::new()
         .api_route_with(
             "/event-type/",
-            post(create_event_type).get(list_event_types),
-            openapi_tag("Event Type"),
+            post_with(create_event_type, create_event_type_operation)
+                .get_with(list_event_types, list_event_types_operation),
+            &tag,
         )
         .api_route_with(
             "/event-type/:event_type_name/",
-            get(get_event_type)
-                .put(update_event_type)
-                .patch(patch_event_type)
-                .delete(delete_event_type),
-            openapi_tag("Event Type"),
+            get_with(get_event_type, get_event_type_operation)
+                .put_with(update_event_type, update_event_type_operation)
+                .patch_with(patch_event_type, patch_event_type_operation)
+                .delete_with(delete_event_type, delete_event_type_operation),
+            &tag,
         )
         .api_route_with(
             "/event-type/:event_type_name/retry-schedule/",
@@ -433,8 +460,11 @@ pub fn router() -> ApiRouter<AppState> {
         )
         .api_route_with(
             "/event-type/schema/generate-example/",
-            post(api_not_implemented),
-            openapi_tag("Event Type"),
+            post_with(
+                api_not_implemented,
+                openapi_desc(GENERATE_SCHEMA_EXAMPLE_DESCRIPTION),
+            ),
+            tag,
         )
 }
 
diff --git a/server/svix-server/src/v1/endpoints/message.rs b/server/svix-server/src/v1/endpoints/message.rs
index 519ddb6c7..2134b3666 100644
--- a/server/svix-server/src/v1/endpoints/message.rs
+++ b/server/svix-server/src/v1/endpoints/message.rs
@@ -6,22 +6,22 @@ use crate::{
         message_app::CreateMessageApp,
         permissions,
         types::{
-            ApplicationIdOrUid, EventChannel, EventChannelSet, EventTypeName, EventTypeNameSet,
-            MessageAttemptTriggerType, MessageId, MessageIdOrUid, MessageUid,
+            EventChannel, EventChannelSet, EventTypeName, EventTypeNameSet,
+            MessageAttemptTriggerType, MessageId, MessageUid,
         },
     },
     ctx, err_generic,
     error::{HttpError, Result},
     queue::MessageTaskBatch,
     v1::utils::{
-        apply_pagination, iterator_from_before_or_after, openapi_tag, validation_error,
-        ListResponse, MessageListFetchOptions, ModelIn, ModelOut, PaginationLimit,
-        ReversibleIterator, ValidatedJson, ValidatedQuery,
+        apply_pagination_desc, iterator_from_before_or_after, openapi_tag, validation_error,
+        ApplicationMsgPath, EventTypesQuery, JsonStatus, ListResponse, ModelIn, ModelOut,
+        PaginationLimit, ReversibleIterator, ValidatedJson, ValidatedQuery,
     },
     AppState,
 };
 use aide::axum::{
-    routing::{get, post},
+    routing::{delete_with, get_with, post_with},
     ApiRouter,
 };
 use axum::{
@@ -31,12 +31,12 @@ use axum::{
 use chrono::{DateTime, Duration, Utc};
 use hyper::StatusCode;
 use schemars::JsonSchema;
-use sea_orm::entity::prelude::*;
 use sea_orm::ActiveModelTrait;
+use sea_orm::{entity::prelude::*, IntoActiveModel};
 use sea_orm::{sea_query::Expr, ActiveValue::Set};
 use serde::{Deserialize, Serialize};
 
-use svix_server_derive::{ModelIn, ModelOut};
+use svix_server_derive::{aide_annotate, ModelIn, ModelOut};
 use validator::{Validate, ValidationError};
 
 use crate::db::models::message;
@@ -165,36 +165,44 @@ pub struct ListMessagesQueryParams {
     #[serde(default = "default_true")]
     with_content: bool,
 
+    before: Option<DateTime<Utc>>,
     after: Option<DateTime<Utc>>,
 }
 
+/// List all of the application's messages.
+///
+/// The `before` parameter lets you filter all items created before a certain date and is ignored if an iterator is passed.
+/// The `after` parameter lets you filter all items created after a certain date and is ignored if an iterator is passed.
+/// `before` and `after` cannot be used simultaneously.
+#[aide_annotate(op_id = "list_messages_api_v1_app__app_id__msg__get")]
 async fn list_messages(
     State(AppState { ref db, .. }): State<AppState>,
     ValidatedQuery(pagination): ValidatedQuery<Pagination<ReversibleIterator<MessageId>>>,
     ValidatedQuery(ListMessagesQueryParams {
         channel,
         with_content,
+        before,
         after,
     }): ValidatedQuery<ListMessagesQueryParams>,
-    list_filter: MessageListFetchOptions,
+    EventTypesQuery(event_types): EventTypesQuery,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<ListResponse<MessageOut>>> {
     let PaginationLimit(limit) = pagination.limit;
 
     let mut query = message::Entity::secure_find(app.id);
 
-    if let Some(EventTypeNameSet(event_types)) = list_filter.event_types {
+    if let Some(EventTypeNameSet(event_types)) = event_types {
         query = query.filter(message::Column::EventType.is_in(event_types));
     }
 
     if let Some(channel) = channel {
-        query = query.filter(Expr::cust_with_values("channels ?? ?", vec![channel]));
+        query = query.filter(Expr::cust_with_values("channels @> $1", [channel.jsonb()]));
     }
 
-    let iterator = iterator_from_before_or_after(pagination.iterator, list_filter.before, after);
+    let iterator = iterator_from_before_or_after(pagination.iterator, before, after);
     let is_prev = matches!(iterator, Some(ReversibleIterator::Prev(_)));
 
-    let query = apply_pagination(query, message::Column::Id, limit, iterator);
+    let query = apply_pagination_desc(query, message::Column::Id, limit, iterator);
     let into = |x: message::Model| {
         if with_content {
             x.into()
@@ -203,17 +211,13 @@ async fn list_messages(
         }
     };
 
-    let out = if is_prev {
-        ctx!(query.all(db).await)?
-            .into_iter()
-            .rev()
-            .map(into)
-            .collect()
-    } else {
-        ctx!(query.all(db).await)?.into_iter().map(into).collect()
-    };
+    let out = ctx!(query.all(db).await)?.into_iter().map(into).collect();
 
-    Ok(Json(MessageOut::list_response(out, limit as usize, false)))
+    Ok(Json(MessageOut::list_response(
+        out,
+        limit as usize,
+        is_prev,
+    )))
 }
 
 #[derive(Debug, Deserialize, Validate, JsonSchema)]
@@ -222,6 +226,16 @@ pub struct CreateMessageQueryParams {
     with_content: bool,
 }
 
+/// Creates a new message and dispatches it to all of the application's endpoints.
+///
+/// The `eventId` is an optional custom unique ID. It's verified to be unique only up to a day, after that no verification will be made.
+/// If a message with the same `eventId` already exists for any application in your environment, a 409 conflict error will be returned.
+///
+/// The `eventType` indicates the type and schema of the event. All messages of a certain `eventType` are expected to have the same schema. Endpoints can choose to only listen to specific event types.
+/// Messages can also have `channels`, which similar to event types let endpoints filter by them. Unlike event types, messages can have multiple channels, and channels don't imply a specific message content or schema.
+///
+/// The `payload` property is the webhook's body (the actual webhook message). Svix supports payload sizes of up to ~350kb, though it's generally a good idea to keep webhook payloads small, probably no larger than 40kb.
+#[aide_annotate(op_id = "create_message_api_v1_app__app_id__msg__post")]
 async fn create_message(
     State(AppState {
         ref db,
@@ -234,7 +248,7 @@ async fn create_message(
     >,
     permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication,
     ValidatedJson(data): ValidatedJson<MessageIn>,
-) -> Result<(StatusCode, Json<MessageOut>)> {
+) -> Result<JsonStatus<202, MessageOut>> {
     let create_message_app = CreateMessageApp::layered_fetch(
         &cache,
         db,
@@ -277,7 +291,7 @@ async fn create_message(
         MessageOut::without_payload(msg)
     };
 
-    Ok((StatusCode::ACCEPTED, Json(msg_out)))
+    Ok(JsonStatus(msg_out))
 }
 
 #[derive(Debug, Deserialize, Validate, JsonSchema)]
@@ -285,9 +299,12 @@ pub struct GetMessageQueryParams {
     #[serde(default = "default_true")]
     with_content: bool,
 }
+
+/// Get a message by its ID or eventID.
+#[aide_annotate(op_id = "get_message_api_v1_app__app_id__msg__msg_id___get")]
 async fn get_message(
     State(AppState { ref db, .. }): State<AppState>,
-    Path((_app_id, msg_id)): Path<(ApplicationIdOrUid, MessageIdOrUid)>,
+    Path(ApplicationMsgPath { msg_id, .. }): Path<ApplicationMsgPath>,
     ValidatedQuery(GetMessageQueryParams { with_content }): ValidatedQuery<GetMessageQueryParams>,
     permissions::Application { app }: permissions::Application,
 ) -> Result<Json<MessageOut>> {
@@ -305,17 +322,47 @@ async fn get_message(
     Ok(Json(msg_out))
 }
 
+/// Delete the given message's payload. Useful in cases when a message was accidentally sent with sensitive content.
+///
+/// The message can't be replayed or resent once its payload has been deleted or expired.
+#[aide_annotate(op_id = "expunge_message_payload_api_v1_app__app_id__msg__msg_id__content__delete")]
+async fn expunge_message_content(
+    State(AppState { ref db, .. }): State<AppState>,
+    Path(ApplicationMsgPath { msg_id, .. }): Path<ApplicationMsgPath>,
+    permissions::OrganizationWithApplication { app }: permissions::OrganizationWithApplication,
+) -> Result<StatusCode> {
+    let mut msg = ctx!(
+        message::Entity::secure_find_by_id_or_uid(app.id, msg_id)
+            .one(db)
+            .await
+    )?
+    .ok_or_else(|| HttpError::not_found(None, None))?
+    .into_active_model();
+
+    msg.payload = Set(None);
+    ctx!(msg.update(db).await)?;
+
+    Ok(StatusCode::NO_CONTENT)
+}
+
 pub fn router() -> ApiRouter<AppState> {
+    let tag = openapi_tag("Message");
     ApiRouter::new()
         .api_route_with(
             "/app/:app_id/msg/",
-            post(create_message).get(list_messages),
-            openapi_tag("Message"),
+            post_with(create_message, create_message_operation)
+                .get_with(list_messages, list_messages_operation),
+            &tag,
         )
         .api_route_with(
             "/app/:app_id/msg/:msg_id/",
-            get(get_message),
-            openapi_tag("Message"),
+            get_with(get_message, get_message_operation),
+            &tag,
+        )
+        .api_route_with(
+            "/app/:app_id/msg/:msg_id/content/",
+            delete_with(expunge_message_content, expunge_message_content_operation),
+            tag,
         )
 }
 
diff --git a/server/svix-server/src/v1/utils/mod.rs b/server/svix-server/src/v1/utils/mod.rs
index 12cf0eb65..2858c2601 100644
--- a/server/svix-server/src/v1/utils/mod.rs
+++ b/server/svix-server/src/v1/utils/mod.rs
@@ -6,19 +6,22 @@ use std::{
     collections::HashSet,
     error::Error as StdError,
     ops::Deref,
-    str::FromStr,
     time::{SystemTime, UNIX_EPOCH},
 };
 
-use aide::{transform::TransformPathItem, OperationInput, OperationIo};
+use aide::{
+    transform::{TransformOperation, TransformPathItem},
+    OperationInput, OperationIo, OperationOutput,
+};
 use axum::{
     async_trait,
     body::HttpBody,
     extract::{FromRequest, FromRequestParts, Query},
+    response::IntoResponse,
     BoxError,
 };
 use chrono::{DateTime, Utc};
-use http::{request::Parts, Request};
+use http::{request::Parts, Request, StatusCode};
 use regex::Regex;
 use schemars::JsonSchema;
 use sea_orm::{ColumnTrait, QueryFilter, QueryOrder, QuerySelect};
@@ -27,7 +30,10 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use validator::{Validate, ValidationError};
 
 use crate::{
-    core::types::{BaseId, EventTypeName, EventTypeNameSet},
+    core::types::{
+        ApplicationIdOrUid, BaseId, EndpointIdOrUid, EventTypeName, EventTypeNameSet,
+        MessageAttemptId, MessageIdOrUid,
+    },
     error::{Error, HttpError, Result, ValidationErrorItem},
 };
 
@@ -51,6 +57,7 @@ pub struct Pagination<T: Validate + JsonSchema> {
     pub limit: PaginationLimit,
     #[validate]
     pub iterator: Option<T>,
+    pub order: Option<ListOrdering>,
 }
 
 #[derive(Debug, JsonSchema)]
@@ -143,12 +150,12 @@ pub fn iterator_from_before_or_after<I: BaseId<Output = I> + Validate>(
     iterator.or_else(|| {
         before
             .map(|time| ReversibleIterator::Normal(I::start_id(time)))
-            .or_else(|| after.map(|time| ReversibleIterator::Prev(I::end_id(time))))
+            .or_else(|| after.map(|time| ReversibleIterator::Prev(I::start_id(time))))
     })
 }
 
 /// Applies sorting and filtration to a query from its iterator, sort column, and limit
-pub fn apply_pagination<
+pub fn apply_pagination_desc<
     Q: QuerySelect + QueryOrder + QueryFilter,
     C: ColumnTrait,
     I: BaseId<Output = I> + Validate + Into<sea_orm::Value>,
@@ -173,12 +180,54 @@ pub fn apply_pagination<
     }
 }
 
-#[derive(Serialize)]
+/// Marker trait for any type that is used for iterating through results
+/// in the public API.
+pub trait IdIterator: Validate + Into<sea_orm::Value> {}
+
+impl<T: BaseId + Validate + Into<sea_orm::Value>> IdIterator for T {}
+impl IdIterator for EventTypeName {}
+
+pub fn apply_pagination<
+    Q: QuerySelect + QueryOrder + QueryFilter,
+    C: ColumnTrait,
+    I: IdIterator,
+>(
+    query: Q,
+    sort_column: C,
+    limit: u64,
+    iterator: Option<ReversibleIterator<I>>,
+    ordering: ListOrdering,
+) -> Q {
+    use ListOrdering::*;
+    use ReversibleIterator::*;
+
+    let query = query.limit(limit + 1);
+
+    let iterator = if let Some(it) = iterator {
+        it
+    } else {
+        return match ordering {
+            Ascending => query.order_by_asc(sort_column),
+            Descending => query.order_by_desc(sort_column),
+        };
+    };
+
+    match (iterator, ordering) {
+        (Prev(id), Ascending) | (Normal(id), Descending) => {
+            query.order_by_desc(sort_column).filter(sort_column.lt(id))
+        }
+        (Prev(id), Descending) | (Normal(id), Ascending) => {
+            query.order_by_asc(sort_column).filter(sort_column.gt(id))
+        }
+    }
+}
+
+#[derive(Serialize, JsonSchema)]
 pub struct EmptyResponse {}
 
 #[derive(Serialize, Deserialize, Clone, JsonSchema)]
 #[serde(rename_all = "camelCase")]
-pub struct ListResponse<T: Clone> {
+pub struct ListResponse<T> {
     pub data: Vec<T>,
     pub iterator: Option<String>,
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -192,16 +241,33 @@ pub trait ModelIn {
     fn update_model(self, model: &mut Self::ActiveModel);
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "camelCase")]
+pub enum ListOrdering {
+    Ascending,
+    Descending,
+}
+
+#[derive(PartialEq, Eq)]
+enum IteratorDirection {
+    Next,
+    Previous,
+}
+
 fn list_response_inner<T: ModelOut>(
     mut data: Vec<T>,
     limit: usize,
-    is_prev_iter: bool,
+    iter_direction: IteratorDirection,
     supports_prev_iterator: bool,
 ) -> ListResponse<T> {
     let done = data.len() <= limit;
 
+    if iter_direction == IteratorDirection::Previous {
+        data.reverse();
+    }
+
     if data.len() > limit {
-        if is_prev_iter {
+        if iter_direction == IteratorDirection::Previous {
             data = data.drain(data.len() - limit..).collect();
         } else {
             data.truncate(limit);
@@ -223,15 +289,20 @@ fn list_response_inner<T: ModelOut>(
     }
 }
 
-pub trait ModelOut: Clone {
+pub trait ModelOut: Sized {
     fn id_copy(&self) -> String;
 
     fn list_response(data: Vec<Self>, limit: usize, is_prev_iter: bool) -> ListResponse<Self> {
-        list_response_inner(data, limit, is_prev_iter, true)
+        let direction = if is_prev_iter {
+            IteratorDirection::Previous
+        } else {
+            IteratorDirection::Next
+        };
+        list_response_inner(data, limit, direction, true)
     }
 
     fn list_response_no_prev(data: Vec<Self>, limit: usize) -> ListResponse<Self> {
-        list_response_inner(data, limit, false, false)
+        list_response_inner(data, limit, IteratorDirection::Next, false)
     }
 }
 
@@ -379,53 +450,44 @@ impl<T: JsonSchema> OperationInput for ValidatedQuery<T> {
     }
 }
 
-/// This struct is slower than Query. Only use this if we need to pass arrays.
-#[derive(Debug)]
-pub struct MessageListFetchOptions {
-    pub event_types: Option<EventTypeNameSet>,
-    pub before: Option<DateTime<Utc>>,
-}
+// A special wrapper to handle query parameter lists. serde_qs and serde_urlencode can't
+// handle url query param arrays as flexibly as we need to support in our API
+pub struct EventTypesQuery(pub Option<EventTypeNameSet>);
 
 #[async_trait]
-impl<S> FromRequestParts<S> for MessageListFetchOptions
+impl<S> FromRequestParts<S> for EventTypesQuery
 where
     S: Send + Sync,
 {
     type Rejection = Error;
 
     async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self> {
-        let pairs: Vec<(String, String)> =
-            serde_urlencoded::from_str(parts.uri.query().unwrap_or_default())
-                .map_err(|err| HttpError::bad_request(None, Some(err.to_string())))?;
-
-        let mut before = None;
-        let mut event_types = EventTypeNameSet(HashSet::<EventTypeName>::new());
-        for (key, value) in pairs {
-            if key == "event_types" {
-                event_types.0.insert(EventTypeName(value));
-            } else if key == "before" {
-                before = Some(DateTime::<Utc>::from_str(&value).map_err(|_| {
-                    HttpError::unprocessable_entity(vec![ValidationErrorItem {
-                        loc: vec!["query".to_owned(), "before".to_owned()],
-                        msg: "Unable to parse before".to_owned(),
-                        ty: "value_error".to_owned(),
-                    }])
-                })?);
-            }
-        }
-        let event_types = if event_types.0.is_empty() {
-            None
+        let pairs = form_urlencoded::parse(parts.uri.query().unwrap_or_default().as_bytes());
+
+        let event_types: HashSet<EventTypeName> = pairs
+            .filter_map(|(key, value)| {
+                // want to handle both `?event_types=`, `?event_types[]=`, and `?event_types[1]=`
+                if key == "event_types" || (key.starts_with("event_types[") && key.ends_with(']')) {
+                    Some(EventTypeName(value.into_owned()))
+                } else {
+                    None
+                }
+            })
+            .collect();
+
+        if event_types.is_empty() {
+            Ok(Self(None))
         } else {
-            Some(event_types)
-        };
-        Ok(MessageListFetchOptions {
-            event_types,
-            before,
-        })
+            let event_types = EventTypeNameSet(event_types);
+            event_types.validate().map_err(|e| {
+                HttpError::unprocessable_entity(validation_errors(vec!["query".to_owned()], e))
+            })?;
+            Ok(Self(Some(event_types)))
+        }
     }
 }
 
-impl OperationInput for MessageListFetchOptions {}
+impl OperationInput for EventTypesQuery {}
 
 pub async fn api_not_implemented() -> Result<()> {
     Err(HttpError::not_implemented(None, None).into())
@@ -451,8 +513,12 @@ pub fn validate_no_control_characters_unrequired(
     }
 }
 
-pub fn openapi_tag(tag: &'static str) -> impl FnOnce(TransformPathItem) -> TransformPathItem {
-    |op| op.tag(tag)
+pub fn openapi_tag<T: AsRef<str>>(tag: T) -> impl Fn(TransformPathItem) -> TransformPathItem {
+    move |op| op.tag(tag.as_ref())
+}
+
+pub fn openapi_desc<T: AsRef<str>>(desc: T) -> impl Fn(TransformOperation) -> TransformOperation {
+    move |op| op.description(desc.as_ref())
 }
 
 pub fn get_unix_timestamp() -> u64 {
@@ -462,6 +528,124 @@ pub fn get_unix_timestamp() -> u64 {
         .as_secs()
 }
 
+#[derive(Deserialize, JsonSchema)]
+pub struct ApplicationPath {
+    pub app_id: ApplicationIdOrUid,
+}
+
+#[derive(Deserialize, JsonSchema)]
+pub struct ApplicationEndpointPath {
+    pub app_id: ApplicationIdOrUid,
+    pub endpoint_id: EndpointIdOrUid,
+}
+
+#[derive(Deserialize, JsonSchema)]
+pub struct ApplicationMsgPath {
+    pub app_id: ApplicationIdOrUid,
+    pub msg_id: MessageIdOrUid,
+}
+
+#[derive(Deserialize, JsonSchema)]
+pub struct ApplicationMsgEndpointPath {
+    pub app_id: ApplicationIdOrUid,
+    pub msg_id: MessageIdOrUid,
+    pub endpoint_id: EndpointIdOrUid,
+}
+
+#[derive(Deserialize, JsonSchema)]
+pub struct ApplicationMsgAttemptPath {
+    pub app_id: ApplicationIdOrUid,
+    pub msg_id: MessageIdOrUid,
+    pub attempt_id: MessageAttemptId,
+}
+
+#[derive(Deserialize, JsonSchema)]
+pub struct EventTypeNamePath {
+    pub event_type_name: EventTypeName,
+}
+
+/// JsonStatus is a wrapper over `axum::extract::Json` as a handler output.
+/// Setting the `STATUS` const parameter automatically sets the response
+/// status code, as well as inserting it into the aide documentation.
+pub struct JsonStatus<const STATUS: u16, T: JsonSchema + Serialize>(pub T);
+
+impl<const STATUS: u16, T: JsonSchema + Serialize> IntoResponse for JsonStatus<STATUS, T> {
+    fn into_response(self) -> axum::response::Response {
+        (
+            StatusCode::from_u16(STATUS).unwrap(),
+            axum::extract::Json(self.0),
+        )
+            .into_response()
+    }
+}
+
+impl<const STATUS: u16, T: JsonSchema + Serialize> OperationOutput for JsonStatus<STATUS, T> {
+    type Inner = T;
+
+    fn operation_response(
+        ctx: &mut aide::gen::GenContext,
+        operation: &mut aide::openapi::Operation,
+    ) -> Option<aide::openapi::Response> {
+        axum::extract::Json::<T>::operation_response(ctx, operation)
+    }
+
+    fn inferred_responses(
+        ctx: &mut aide::gen::GenContext,
+        operation: &mut aide::openapi::Operation,
+    ) -> Vec<(Option<u16>, aide::openapi::Response)> {
+        if let Some(resp) = Self::operation_response(ctx, operation) {
+            vec![(Some(STATUS), resp)]
+        } else {
+            vec![]
+        }
+    }
+}
+
+/// JsonStatusUpsert is a wrapper over `axum::extract::Json` as a handler
+/// output. It is a special casing of `JsonStatus` for situations where a
+/// resource is either being updated or created within the same operation. In
+/// case of `Updated` HTTP 200 OK is returned, in case of `Created` HTTP 201
+/// CREATED is returned.
+pub enum JsonStatusUpsert<T: JsonSchema + Serialize> {
+    Updated(T),
+    Created(T),
+}
+
+impl<T: JsonSchema + Serialize> IntoResponse for JsonStatusUpsert<T> {
+    fn into_response(self) -> axum::response::Response {
+        let (status, body) = match self {
+            JsonStatusUpsert::Updated(v) => (StatusCode::OK, v),
+            JsonStatusUpsert::Created(v) => (StatusCode::CREATED, v),
+        };
+        (status, axum::extract::Json(body)).into_response()
+    }
+}
+
+impl<T: JsonSchema + Serialize> OperationOutput for JsonStatusUpsert<T> {
+    type Inner = T;
+
+    fn operation_response(
+        ctx: &mut aide::gen::GenContext,
+        operation: &mut aide::openapi::Operation,
+    ) -> Option<aide::openapi::Response> {
+        axum::extract::Json::<T>::operation_response(ctx, operation)
+    }
+
+    fn inferred_responses(
+        ctx: &mut aide::gen::GenContext,
+        operation: &mut aide::openapi::Operation,
+    ) -> Vec<(Option<u16>, aide::openapi::Response)> {
+        if let Some(resp) = Self::operation_response(ctx, operation) {
+            vec![
+                (Some(StatusCode::OK.into()), resp.clone()),
+                (Some(StatusCode::CREATED.into()), resp),
+            ]
+        } else {
+            vec![]
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use validator::Validate;
diff --git a/server/svix-server/src/worker.rs b/server/svix-server/src/worker.rs
index 88160acae..25921f1e7 100644
--- a/server/svix-server/src/worker.rs
+++ b/server/svix-server/src/worker.rs
@@ -838,11 +838,17 @@ async fn process_queue_task_inner(
 
     let msg = ctx!(message::Entity::find_by_id(msg_id.clone()).one(db).await)?
         .ok_or_else(|| err_generic!("Unexpected: message doesn't exist"))?;
-    let payload = msg
+    let payload = match msg
         .payload
         .as_ref()
         .and_then(|value| serde_json::to_string(value).ok())
-        .ok_or_else(|| err_generic!("Message payload is NULL"))?;
+    {
+        Some(p) => p,
+        None => {
+            tracing::warn!("Message payload is NULL; payload has most likely expired");
+            return Ok(());
+        }
+    };
 
     span.record("app_id", &msg.app_id.0);
     span.record("org_id", &msg.org_id.0);
diff --git a/server/svix-server_derive/src/lib.rs b/server/svix-server_derive/src/lib.rs
index 53071ccde..e45ded46e 100644
--- a/server/svix-server_derive/src/lib.rs
+++ b/server/svix-server_derive/src/lib.rs
@@ -1,6 +1,9 @@
-use quote::quote;
+use quote::{format_ident, quote, ToTokens};
 
-use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics};
+use syn::{
+    parse_macro_input, parse_quote, AttributeArgs, DeriveInput, GenericParam, Generics, ItemFn,
+    NestedMeta,
+};
 
 #[proc_macro_derive(ModelIn)]
 pub fn derive_model_in(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -62,6 +65,134 @@ pub fn derive_model_out(input: proc_macro::TokenStream) -> proc_macro::TokenStre
     proc_macro::TokenStream::from(expanded)
 }
 
+fn doc_comment_from_attributes(attributes: &Vec<syn::Attribute>) -> Option<String> {
+    let mut doc_comment_lines = Vec::new();
+
+    for attr in attributes {
+        let meta = attr
+            .parse_meta()
+            .expect("Failed to parse fn attribute as Meta");
+
+        if let syn::Meta::NameValue(meta) = meta {
+            if meta.path.to_token_stream().to_string() != "doc" {
+                continue;
+            }
+            if let syn::Lit::Str(doc) = meta.lit {
+                doc_comment_lines.push(doc.value().trim().to_string());
+            }
+        }
+    }
+
+    if doc_comment_lines.is_empty() {
+        return None;
+    }
+    Some(doc_comment_lines.join("\n"))
+}
+
+fn title_case(s: &str) -> String {
+    let mut c = s.chars();
+    match c.next() {
+        None => String::new(),
+        Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
+    }
+}
+
+#[proc_macro_attribute]
+/// Attribute macro for axum/aide handler functions that creates a new function
+/// with the same name as the handler, suffixed with `_operation`, that acts as
+/// an operation transformation function, automatically setting the operation
+/// ID, summary and description.
+///
+/// # Example
+/// ```
+/// /// This is foo!
+/// #[aide_annotate]
+/// fn foo() {
+/// }
+///
+/// /// This is bar, with a custom op ID and summary
+/// #[aide_annotate(op_id = "custom_id", op_summary = "Bar Operation!")]
+/// fn bar() {
+/// }
+/// ```
+pub fn aide_annotate(
+    args: proc_macro::TokenStream,
+    input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    let args = parse_macro_input!(args as AttributeArgs);
+
+    let item = parse_macro_input!(input as ItemFn);
+
+    // By default, use the function's name as the operation id.
+    let mut operation_id = item.sig.ident.to_string();
+    // The operation summary is the title-cased version of the original
+    // function name.
+    let mut operation_summary = operation_id
+        .split('_')
+        .map(title_case)
+        .collect::<Vec<String>>()
+        .join(" ");
+    // The documentation function's name will always be the name of the
+    // original function suffixed with `_operation`.
+    let operation_ident = format_ident!("{}_operation", item.sig.ident);
+    let visibility = item.vis.clone();
+
+    // Allow overriding operation ID and summary via arguments
+    for arg in args {
+        if let NestedMeta::Meta(syn::Meta::NameValue(meta)) = arg {
+            let arg_id = meta.path.to_token_stream().to_string();
+
+            let value = if let syn::Lit::Str(s) = meta.lit {
+                s.value()
+            } else {
+                return syn::Error::new_spanned(meta.lit, "Unexpected literal, expected a string")
+                    .into_compile_error()
+                    .into();
+            };
+
+            match arg_id.as_str() {
+                "op_id" => operation_id = value,
+                "op_summary" => operation_summary = value,
+                _ => {
+                    let path = meta.path.to_token_stream().to_string();
+                    let msg =
+                        format!("Unknown argument `{path}`, expected `op_id` or `op_summary`",);
+                    return syn::Error::new_spanned(meta.path, msg)
+                        .into_compile_error()
+                        .into();
+                }
+            }
+        } else {
+            return syn::Error::new_spanned(arg, "Unexpected argument")
+                .into_compile_error()
+                .into();
+        }
+    }
+
+    let description = doc_comment_from_attributes(&item.attrs);
+
+    if description.is_none() {
+        let msg = "An annotated handler must have a doc comment for its description.";
+        return syn::Error::new(item.sig.ident.span(), msg)
+            .into_compile_error()
+            .into();
+    }
+
+    let f = item.into_token_stream();
+
+    let out = quote! {
+        #f
+
+        #visibility fn #operation_ident(op: ::aide::transform::TransformOperation) -> ::aide::transform::TransformOperation {
+            op
+                .id(#operation_id)
+                .summary(#operation_summary)
+                .description(#description)
+        }
+    };
+    proc_macro::TokenStream::from(out)
+}
+
 // Add a bound `T: HeapSize` to every type parameter T.
 fn add_trait_bounds(mut generics: Generics) -> Generics {
     for param in &mut generics.params {


Test patch:
diff --git a/server/svix-server/tests/e2e_application.rs b/server/svix-server/tests/e2e_application.rs
index db1ac3280..9cfddf538 100644
--- a/server/svix-server/tests/e2e_application.rs
+++ b/server/svix-server/tests/e2e_application.rs
@@ -348,6 +348,7 @@ async fn test_list() {
         "api/v1/app/",
         |i| application_in(&format!("App {i}")),
         true,
+        true,
     )
     .await
     .unwrap();
diff --git a/server/svix-server/tests/e2e_attempt.rs b/server/svix-server/tests/e2e_attempt.rs
index 2dfe258fd..f6596561b 100644
--- a/server/svix-server/tests/e2e_attempt.rs
+++ b/server/svix-server/tests/e2e_attempt.rs
@@ -18,15 +18,76 @@ mod utils;
 
 use utils::{
     common_calls::{
-        create_test_app, create_test_endpoint, create_test_message, endpoint_in,
-        get_msg_attempt_list_and_assert_count,
+        create_test_app, create_test_endpoint, create_test_message, create_test_msg_with,
+        endpoint_in, get_msg_attempt_list_and_assert_count,
     },
     get_default_test_config, run_with_retries, start_svix_server, start_svix_server_with_cfg,
-    TestReceiver,
+    IgnoredResponse, TestReceiver,
 };
 
 use std::time::Duration;
 
+#[tokio::test]
+async fn test_expunge_attempt_response_body() {
+    let (client, _jh) = start_svix_server().await;
+
+    let app_id = create_test_app(&client, "app1").await.unwrap().id;
+
+    let sensitive_response_json = serde_json::json!({"sensitive":"data"});
+    let mut receiver = TestReceiver::start_with_body(
+        axum::http::StatusCode::OK,
+        axum::Json(sensitive_response_json.clone()),
+    );
+
+    let endpoint_id = create_test_endpoint(&client, &app_id, &receiver.endpoint)
+        .await
+        .unwrap()
+        .id;
+
+    let msg_id = create_test_message(&client, &app_id, serde_json::json!({"test": "data1"}))
+        .await
+        .unwrap()
+        .id;
+
+    receiver.data_recv.recv().await;
+
+    let attempt = run_with_retries(|| async {
+        let attempts: ListResponse<MessageAttemptOut> = client
+            .get(
+                &format!("api/v1/app/{app_id}/attempt/endpoint/{endpoint_id}/"),
+                StatusCode::OK,
+            )
+            .await
+            .unwrap();
+        assert_eq!(1, attempts.data.len());
+        Ok(attempts.data[0].clone())
+    })
+    .await
+    .unwrap();
+
+    let attempt_response: serde_json::Value = serde_json::from_str(&attempt.response).unwrap();
+    assert_eq!(sensitive_response_json, attempt_response);
+
+    let attempt_id = &attempt.id;
+    let _: IgnoredResponse = client
+        .delete(
+            &format!("api/v1/app/{app_id}/msg/{msg_id}/attempt/{attempt_id}/content/"),
+            StatusCode::NO_CONTENT,
+        )
+        .await
+        .unwrap();
+
+    let attempt: MessageAttemptOut = client
+        .get(
+            &format!("api/v1/app/{app_id}/msg/{msg_id}/attempt/{attempt_id}/"),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+
+    assert_eq!("EXPUNGED", &attempt.response);
+}
+
 #[tokio::test]
 async fn test_list_attempted_messages() {
     let (client, _jh) = start_svix_server().await;
@@ -41,7 +102,7 @@ async fn test_list_attempted_messages() {
         .unwrap()
         .id;
 
-    // Let's have an endponit with a UID too
+    // Let's have an endpoint with a UID too
     let mut endp2 = endpoint_in(&receiver_2.endpoint);
     endp2.uid = Some(EndpointUid("test".to_owned()));
     let endp_id_2 = client
@@ -60,9 +121,14 @@ async fn test_list_attempted_messages() {
     let msg_2 = create_test_message(&client, &app_id, serde_json::json!({"test": "data2"}))
         .await
         .unwrap();
-    let msg_3 = create_test_message(&client, &app_id, serde_json::json!({"test": "data3"}))
-        .await
-        .unwrap();
+    let msg_3 = create_test_msg_with(
+        &client,
+        &app_id,
+        serde_json::json!({"test": "data3"}),
+        "balloon.popped",
+        ["news"],
+    )
+    .await;
 
     run_with_retries(|| async {
         let list_1: ListResponse<AttemptedMessageOut> = client
@@ -102,6 +168,16 @@ async fn test_list_attempted_messages() {
     })
     .await
     .unwrap();
+
+    let list_filtered: ListResponse<AttemptedMessageOut> = client
+        .get(
+            &format!("api/v1/app/{app_id}/endpoint/{endp_id_1}/msg/?channel=news"),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(list_filtered.data.len(), 1);
+    assert!(list_filtered.data[0].msg == msg_3);
 }
 
 #[tokio::test]
@@ -131,9 +207,14 @@ async fn test_list_attempts_by_endpoint() {
     let msg_2 = create_test_message(&client, &app_id, serde_json::json!({"test": "data2"}))
         .await
         .unwrap();
-    let msg_3 = create_test_message(&client, &app_id, serde_json::json!({"test": "data3"}))
-        .await
-        .unwrap();
+    let msg_3 = create_test_msg_with(
+        &client,
+        &app_id,
+        serde_json::json!({"test": "data3"}),
+        "user.exploded",
+        ["obits"],
+    )
+    .await;
 
     // And wait at most one second for all attempts to be processed
     run_with_retries(|| async {
@@ -178,6 +259,51 @@ async fn test_list_attempts_by_endpoint() {
         assert!(message_ids.contains(&msg_3.id));
     }
 
+    let foo_attempts: ListResponse<MessageAttemptOut> = client
+        .get(
+            &format!("api/v1/app/{app_id}/attempt/endpoint/{endp_id_2}/?channel=foo"),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert!(foo_attempts.data.is_empty());
+
+    let obits_attempts: ListResponse<MessageAttemptOut> = client
+        .get(
+            &format!("api/v1/app/{app_id}/attempt/endpoint/{endp_id_2}/?channel=obits"),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(obits_attempts.data.len(), 1);
+
+    let exploded_attempts: ListResponse<MessageAttemptOut> = client
+        .get(
+            &format!("api/v1/app/{app_id}/attempt/endpoint/{endp_id_2}/?event_types=user.exploded"),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(exploded_attempts.data.len(), 1);
+
+    let regular_attempts: ListResponse<MessageAttemptOut> = client
+        .get(
+            &format!("api/v1/app/{app_id}/attempt/endpoint/{endp_id_2}/?event_types[]=event.type"),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(regular_attempts.data.len(), 2);
+
+    let all_attempts: ListResponse<MessageAttemptOut> = client
+    .get(
+        &format!("api/v1/app/{app_id}/attempt/endpoint/{endp_id_2}/?event_types[0]=event.type&event_types[1]=user.exploded"),
+        StatusCode::OK,
+    )
+    .await
+    .unwrap();
+    assert_eq!(all_attempts.data.len(), 3);
+
     receiver_1.jh.abort();
     receiver_2.jh.abort();
 }
@@ -464,9 +590,9 @@ async fn test_pagination_by_endpoint() {
             )
             .await
             .unwrap();
-        assert_eq!(first_three_by_time.data.len(), 3);
+        assert_eq!(first_three_by_time.data.len(), 4);
         assert_eq!(
-            &all_attempts.data[0..3],
+            &all_attempts.data[0..=3],
             first_three_by_time.data.as_slice()
         );
 
@@ -508,7 +634,7 @@ async fn test_pagination_by_msg() {
     }
 
     let mut messages = Vec::new();
-    for i in 1..=6usize {
+    for i in 1..=5usize {
         messages.push(
             create_test_message(
                 &client,
@@ -521,6 +647,16 @@ async fn test_pagination_by_msg() {
             .unwrap(),
         );
     }
+    messages.push(
+        create_test_msg_with(
+            &client,
+            &app.id,
+            serde_json::json!({"test": "data6"}),
+            "balloon.popped",
+            ["news"],
+        )
+        .await,
+    );
 
     // Wait until all attempts were made
     run_with_retries(|| async {
@@ -536,6 +672,18 @@ async fn test_pagination_by_msg() {
             if list.data.len() != 6 {
                 anyhow::bail!("list len {}, not 6", list.data.len());
             }
+
+            let list_filtered: ListResponse<MessageAttemptOut> = client
+                .get(
+                    &format!(
+                        "api/v1/app/{}/attempt/endpoint/{}/?channel=news",
+                        app.id, endp_id
+                    ),
+                    StatusCode::OK,
+                )
+                .await
+                .unwrap();
+            assert_eq!(list_filtered.data.len(), 1);
         }
 
         Ok(())
diff --git a/server/svix-server/tests/e2e_endpoint.rs b/server/svix-server/tests/e2e_endpoint.rs
index 6162556ef..6d283986c 100644
--- a/server/svix-server/tests/e2e_endpoint.rs
+++ b/server/svix-server/tests/e2e_endpoint.rs
@@ -647,12 +647,121 @@ async fn test_list() {
         &client,
         &format!("api/v1/app/{app_id}/endpoint/"),
         |i| endpoint_in(&format!("https://localhost/{i}")),
+        false,
         true,
     )
     .await
     .unwrap();
 }
 
+#[tokio::test]
+async fn test_endpoint_list_ordering() {
+    let (client, _jh) = start_svix_server().await;
+
+    let app_id = create_test_app(&client, "App1").await.unwrap().id;
+
+    for i in 0..5 {
+        create_test_endpoint(&client, &app_id, &format!("https://test.url/{i}"))
+            .await
+            .unwrap();
+        // Sleep to account for ksuid 4ms resolution
+        tokio::time::sleep(Duration::from_millis(5)).await;
+    }
+
+    let first_list: ListResponse<EndpointOut> = client
+        .get(
+            &format!("api/v1/app/{}/endpoint/?limit=2", &app_id),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+
+    // First iterate through in order
+    assert_eq!(
+        first_list.data.first().unwrap().ep.url,
+        "https://test.url/4"
+    );
+    assert_eq!(first_list.data.last().unwrap().ep.url, "https://test.url/3");
+    assert!(!first_list.done);
+
+    let list: ListResponse<EndpointOut> = client
+        .get(
+            &format!(
+                "api/v1/app/{}/endpoint/?limit=2&iterator={}",
+                &app_id,
+                first_list.iterator.unwrap()
+            ),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+
+    assert_eq!(list.data.first().unwrap().ep.url, "https://test.url/2");
+    assert_eq!(list.data.last().unwrap().ep.url, "https://test.url/1");
+    assert!(!list.done);
+
+    // Iterate with previous iterator
+    let list: ListResponse<EndpointOut> = client
+        .get(
+            &format!(
+                "api/v1/app/{}/endpoint/?iterator={}",
+                &app_id,
+                list.prev_iterator.unwrap()
+            ),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+
+    assert_eq!(list.data.first().unwrap().ep.url, "https://test.url/4");
+    assert_eq!(list.data.last().unwrap().ep.url, "https://test.url/3");
+    assert!(list.done);
+
+    // Iterate in ascending order
+    let list: ListResponse<EndpointOut> = client
+        .get(
+            &format!("api/v1/app/{}/endpoint/?limit=3&order=ascending", &app_id),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+
+    assert_eq!(list.data.first().unwrap().ep.url, "https://test.url/0");
+    assert_eq!(list.data.last().unwrap().ep.url, "https://test.url/2");
+    assert!(!list.done);
+
+    let list: ListResponse<EndpointOut> = client
+        .get(
+            &format!(
+                "api/v1/app/{}/endpoint/?limit=3&order=ascending&iterator={}",
+                &app_id,
+                list.iterator.unwrap(),
+            ),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+
+    assert_eq!(list.data.first().unwrap().ep.url, "https://test.url/3");
+    assert_eq!(list.data.last().unwrap().ep.url, "https://test.url/4");
+    assert!(list.done);
+
+    // Previous iterator on descending order
+    let list: ListResponse<EndpointOut> = client
+        .get(
+            &format!(
+                "api/v1/app/{}/endpoint/?limit=2&order=ascending&iterator={}",
+                &app_id,
+                list.prev_iterator.unwrap(),
+            ),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(list.data.first().unwrap().ep.url, "https://test.url/1");
+    assert_eq!(list.data.last().unwrap().ep.url, "https://test.url/2");
+}
+
 /// Tests that there is at most one endpoint with a single UID for all endpoints associated with
 /// any application
 #[tokio::test]
@@ -1930,7 +2039,7 @@ async fn test_msg_channels_filter() {
 
     let app_id = create_test_app(&client, "app1").await.unwrap().id;
 
-    let _receiver = TestReceiver::start(StatusCode::OK);
+    let receiver = TestReceiver::start(StatusCode::OK);
 
     let ec = EventChannelSet(HashSet::from([EventChannel("tag1".to_owned())]));
 
@@ -1940,6 +2049,7 @@ async fn test_msg_channels_filter() {
             &app_id,
             EndpointIn {
                 channels,
+                url: Url::parse(&receiver.endpoint).unwrap(),
                 ..default_test_endpoint()
             },
         )
@@ -1963,8 +2073,6 @@ async fn test_msg_channels_filter() {
             .await
             .unwrap();
 
-        tokio::time::sleep(Duration::from_millis(100)).await;
-
         let _list =
             get_msg_attempt_list_and_assert_count(&client, &app_id, &msg.id, expected_count)
                 .await
diff --git a/server/svix-server/tests/e2e_event_type.rs b/server/svix-server/tests/e2e_event_type.rs
index 4cde72677..ed9fa3c1e 100644
--- a/server/svix-server/tests/e2e_event_type.rs
+++ b/server/svix-server/tests/e2e_event_type.rs
@@ -301,6 +301,7 @@ async fn test_list() {
         "api/v1/event-type/",
         |i| event_type_in(&format!("test-event-type-{i}"), None).unwrap(),
         true,
+        false,
     )
     .await
     .unwrap();
diff --git a/server/svix-server/tests/e2e_message.rs b/server/svix-server/tests/e2e_message.rs
index 56169b80e..8d77a0e0d 100644
--- a/server/svix-server/tests/e2e_message.rs
+++ b/server/svix-server/tests/e2e_message.rs
@@ -16,8 +16,8 @@ use svix_server::{
 mod utils;
 
 use utils::{
-    common_calls::{create_test_app, create_test_endpoint, message_in},
-    run_with_retries, start_svix_server, TestReceiver,
+    common_calls::{create_test_app, create_test_endpoint, create_test_msg_with, message_in},
+    run_with_retries, start_svix_server, IgnoredResponse, TestReceiver,
 };
 
 #[tokio::test]
@@ -51,6 +51,14 @@ async fn test_message_create_read_list() {
         )
         .await
         .unwrap();
+    let message_3 = create_test_msg_with(
+        &client,
+        &app_id,
+        serde_json::json!({"test": "data3"}),
+        "balloon.popped",
+        ["news"],
+    )
+    .await;
 
     assert_eq!(
         client
@@ -77,9 +85,20 @@ async fn test_message_create_read_list() {
         .get(&format!("api/v1/app/{}/msg/", &app_id), StatusCode::OK)
         .await
         .unwrap();
-    assert_eq!(list.data.len(), 2);
+    assert_eq!(list.data.len(), 3);
     assert!(list.data.contains(&message_1));
     assert!(list.data.contains(&message_2));
+    assert!(list.data.contains(&message_3));
+
+    let list: ListResponse<MessageOut> = client
+        .get(
+            &format!("api/v1/app/{}/msg/?channel=news", &app_id),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(list.data.len(), 1);
+    assert!(list.data.contains(&message_3));
 }
 
 #[tokio::test]
@@ -380,7 +399,7 @@ async fn test_payload_retention_period() {
         .await
         .unwrap();
 
-    expired_message_cleaner::clean_expired_messages(&pool)
+    expired_message_cleaner::clean_expired_messages(&pool, 5000)
         .await
         .unwrap();
 
@@ -391,3 +410,49 @@ async fn test_payload_retention_period() {
 
     assert_eq!(message.unwrap().payload, None);
 }
+
+#[tokio::test]
+async fn test_expunge_message_payload() {
+    let (client, _jh) = start_svix_server().await;
+
+    let app_id = create_test_app(&client, "testApp").await.unwrap().id;
+
+    let payload = serde_json::json!({"sensitive": "data"});
+    let msg: MessageOut = client
+        .post(
+            &format!("api/v1/app/{}/msg/", &app_id),
+            message_in(&app_id, payload.clone()).unwrap(),
+            StatusCode::ACCEPTED,
+        )
+        .await
+        .unwrap();
+
+    assert_eq!(msg.payload, payload);
+
+    let msg = client
+        .get::<MessageOut>(
+            &format!("api/v1/app/{}/msg/{}/", &app_id, &msg.id),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(msg.payload, payload);
+
+    let _: IgnoredResponse = client
+        .delete(
+            &format!("api/v1/app/{}/msg/{}/content/", &app_id, &msg.id),
+            StatusCode::NO_CONTENT,
+        )
+        .await
+        .unwrap();
+
+    let msg = client
+        .get::<MessageOut>(
+            &format!("api/v1/app/{}/msg/{}/", &app_id, &msg.id),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+
+    assert_eq!(msg.payload, serde_json::json!({"expired": true}));
+}
diff --git a/server/svix-server/tests/e2e_operational_webhooks.rs b/server/svix-server/tests/e2e_operational_webhooks.rs
index 5b549c408..e967bbfd8 100644
--- a/server/svix-server/tests/e2e_operational_webhooks.rs
+++ b/server/svix-server/tests/e2e_operational_webhooks.rs
@@ -20,7 +20,7 @@ use svix_server::{
     },
     v1::endpoints::{
         application::{ApplicationIn, ApplicationOut},
-        endpoint::{EndpointIn, EndpointOut},
+        endpoint::{EndpointIn, EndpointOut, EndpointSecretRotateIn},
     },
 };
 
@@ -236,6 +236,25 @@ async fn test_endpoint_create_update_and_delete() {
         _ => panic!("Got wrong type"),
     };
 
+    // Rotate secrets
+    let _: IgnoredResponse = client_regular
+        .post(
+            &format!(
+                "api/v1/app/{}/endpoint/{}/secret/rotate/",
+                regular_app.id, regular_endp.id
+            ),
+            EndpointSecretRotateIn::default(),
+            StatusCode::NO_CONTENT,
+        )
+        .await
+        .unwrap();
+
+    let op_webhook_out = receiver.data_recv.recv().await.unwrap();
+    assert_eq!(
+        op_webhook_out.get("type").unwrap().as_str().unwrap(),
+        "endpoint.updated"
+    );
+
     // And finally delete the endpoint
     let _: IgnoredResponse = client_regular
         .delete(
diff --git a/server/svix-server/tests/message_app.rs b/server/svix-server/tests/message_app.rs
new file mode 100644
index 000000000..b5e29d81b
--- /dev/null
+++ b/server/svix-server/tests/message_app.rs
@@ -0,0 +1,197 @@
+// SPDX-FileCopyrightText: © 2022 Svix Authors
+// SPDX-License-Identifier: MIT
+
+//! Tests related to the [`CreateMessageApp`] and the [`CreateMessageEndpoint`]s structs.
+use std::time::Duration;
+
+use http::StatusCode;
+
+mod utils;
+
+use svix_server::{
+    cfg::{CacheBackend, CacheType, Configuration},
+    core::{
+        cache::{self, CacheBehavior},
+        message_app::AppEndpointKey,
+        types::{BaseId, OrganizationId},
+    },
+    redis::{new_redis_pool, new_redis_pool_clustered, RedisPool},
+};
+use utils::{
+    common_calls::{create_test_app, create_test_endpoint, create_test_message, message_in},
+    get_default_test_config, start_svix_server_with_cfg_and_org_id, IgnoredResponse, TestReceiver,
+};
+
+pub async fn get_pool(cfg: Configuration) -> RedisPool {
+    match cfg.cache_type {
+        CacheType::RedisCluster => {
+            new_redis_pool_clustered(cfg.redis_dsn.as_ref().unwrap().as_str(), &cfg).await
+        }
+        _ => new_redis_pool(cfg.redis_dsn.as_ref().unwrap().as_str(), &cfg).await,
+    }
+}
+
+/// Ensures that a deleted application returns `None` when using [`layered_fetch`]
+#[tokio::test]
+async fn test_app_deletion() {
+    dotenv::dotenv().ok();
+    let cfg = svix_server::cfg::load().expect("Error loading Configuration");
+    let org_id = OrganizationId::new(None, None);
+    let (client, _jh) =
+        start_svix_server_with_cfg_and_org_id(&get_default_test_config(), org_id.clone()).await;
+
+    // Cannot run test using an in-memory cache as we can't invalidate a key from within the test.
+    // Ie. the Redis backends all share the same memory when a cache is created in this test. The
+    // same is not true for an in-memory map.
+    if matches!(cfg.cache_backend(), CacheBackend::Memory) {
+        return;
+    }
+
+    let mut test_receiver = TestReceiver::start(axum::http::StatusCode::OK);
+
+    let app_id = create_test_app(&client, "TestAppDeletion")
+        .await
+        .unwrap()
+        .id;
+    let _ = create_test_endpoint(&client, &app_id, &test_receiver.endpoint)
+        .await
+        .unwrap();
+
+    let payload = serde_json::json!({"test": "value"});
+
+    create_test_message(&client, &app_id, payload.clone())
+        .await
+        .unwrap();
+
+    assert_eq!(
+        tokio::time::timeout(Duration::from_millis(250), test_receiver.data_recv.recv()).await,
+        Ok(Some(payload.clone()))
+    );
+
+    client
+        .delete::<IgnoredResponse>(&format!("api/v1/app/{app_id}/"), StatusCode::NO_CONTENT)
+        .await
+        .unwrap();
+
+    // Delete the cached [`CreateMessageApp`] here instead of waiting 30s for it to expire
+    let cache = match cfg.cache_backend() {
+        CacheBackend::None => cache::none::new(),
+        CacheBackend::Redis(dsn) => {
+            let mgr = new_redis_pool(dsn, &cfg).await;
+            cache::redis::new(mgr)
+        }
+        CacheBackend::RedisCluster(dsn) => {
+            let mgr = new_redis_pool_clustered(dsn, &cfg).await;
+            cache::redis::new(mgr)
+        }
+
+        // Cannot use memory cache for this test. See the above check.
+        CacheBackend::Memory => unreachable!(),
+    };
+
+    cache
+        .delete(&AppEndpointKey::new(&org_id, &app_id))
+        .await
+        .unwrap();
+
+    // Assert message creation return a 404 with a deleted application
+    client
+        .post::<_, IgnoredResponse>(
+            &format!("api/v1/app/{app_id}/msg/"),
+            message_in("test.event", payload).unwrap(),
+            StatusCode::NOT_FOUND,
+        )
+        .await
+        .unwrap();
+
+    // And assert that no message is sent even if it 404s
+    assert!(
+        tokio::time::timeout(Duration::from_millis(250), test_receiver.data_recv.recv())
+            .await
+            .is_err()
+    );
+}
+
+#[tokio::test]
+async fn test_endp_deletion() {
+    dotenv::dotenv().ok();
+    let cfg = svix_server::cfg::load().expect("Error loading Configuration");
+    let org_id = OrganizationId::new(None, None);
+    let (client, _jh) =
+        start_svix_server_with_cfg_and_org_id(&get_default_test_config(), org_id.clone()).await;
+
+    // Cannot run test using an in-memory cache as we can't invalidate a key from within the test.
+    // Ie. the Redis backends all share the same memory when a cache is created in this test. The
+    // same is not true for an in-memory map.
+    if matches!(cfg.cache_backend(), CacheBackend::Memory) {
+        return;
+    }
+
+    let mut test_receiver = TestReceiver::start(axum::http::StatusCode::OK);
+
+    let app_id = create_test_app(&client, "TestAppDeletion")
+        .await
+        .unwrap()
+        .id;
+    let endp_id = create_test_endpoint(&client, &app_id, &test_receiver.endpoint)
+        .await
+        .unwrap()
+        .id;
+
+    let payload = serde_json::json!({"test": "value"});
+
+    create_test_message(&client, &app_id, payload.clone())
+        .await
+        .unwrap();
+
+    assert_eq!(
+        tokio::time::timeout(Duration::from_millis(250), test_receiver.data_recv.recv()).await,
+        Ok(Some(payload.clone()))
+    );
+
+    client
+        .delete::<IgnoredResponse>(
+            &format!("api/v1/app/{app_id}/endpoint/{endp_id}/"),
+            StatusCode::NO_CONTENT,
+        )
+        .await
+        .unwrap();
+
+    // Delete the cached [`CreateMessageApp`] here instead of waiting 30s for it to expire
+    let cache = match cfg.cache_backend() {
+        CacheBackend::None => cache::none::new(),
+        CacheBackend::Redis(dsn) => {
+            let mgr = new_redis_pool(dsn, &cfg).await;
+            cache::redis::new(mgr)
+        }
+        CacheBackend::RedisCluster(dsn) => {
+            let mgr = new_redis_pool_clustered(dsn, &cfg).await;
+            cache::redis::new(mgr)
+        }
+
+        // Cannot use memory cache for this test. See the above check.
+        CacheBackend::Memory => unreachable!(),
+    };
+
+    cache
+        .delete(&AppEndpointKey::new(&org_id, &app_id))
+        .await
+        .unwrap();
+
+    // Assert message creation return a 202 with a deleted endpoint
+    client
+        .post::<_, IgnoredResponse>(
+            &format!("api/v1/app/{app_id}/msg/"),
+            message_in("test.event", payload).unwrap(),
+            StatusCode::ACCEPTED,
+        )
+        .await
+        .unwrap();
+
+    // But assert that no message is sent as the endpoint no longer exists
+    assert!(
+        tokio::time::timeout(Duration::from_millis(250), test_receiver.data_recv.recv())
+            .await
+            .is_err()
+    );
+}
diff --git a/server/svix-server/tests/utils/common_calls.rs b/server/svix-server/tests/utils/common_calls.rs
index d89a4354b..eaf878479 100644
--- a/server/svix-server/tests/utils/common_calls.rs
+++ b/server/svix-server/tests/utils/common_calls.rs
@@ -1,7 +1,7 @@
 // SPDX-FileCopyrightText: © 2022 Svix Authors
 // SPDX-License-Identifier: MIT
 
-use std::time::Duration;
+use std::{collections::HashSet, time::Duration};
 
 use anyhow::Result;
 use chrono::{DateTime, Utc};
@@ -10,7 +10,10 @@ use reqwest::{StatusCode, Url};
 use serde::{de::DeserializeOwned, Serialize};
 use svix::api::DashboardAccessOut;
 use svix_server::{
-    core::types::{metadata::Metadata, ApplicationId, EventTypeName, FeatureFlagSet, MessageId},
+    core::types::{
+        metadata::Metadata, ApplicationId, EventChannel, EventChannelSet, EventTypeName,
+        FeatureFlagSet, MessageId,
+    },
     v1::{
         endpoints::{
             application::{ApplicationIn, ApplicationOut},
@@ -134,6 +137,40 @@ pub async fn create_test_message(
         .await
 }
 
+pub async fn create_test_msg_with(
+    client: &TestClient,
+    app_id: &ApplicationId,
+    payload: serde_json::Value,
+    event_type: &str,
+    channel: impl IntoIterator<Item = &str>,
+) -> MessageOut {
+    let channels: HashSet<EventChannel> = channel
+        .into_iter()
+        .map(|x| EventChannel(x.to_string()))
+        .collect();
+
+    let channels = if channels.is_empty() {
+        None
+    } else {
+        Some(EventChannelSet(channels))
+    };
+
+    client
+        .post(
+            &format!("api/v1/app/{}/msg/", &app_id),
+            MessageIn {
+                event_type: EventTypeName(event_type.to_owned()),
+                payload,
+                payload_retention_period: 5,
+                channels,
+                uid: None,
+            },
+            StatusCode::ACCEPTED,
+        )
+        .await
+        .unwrap()
+}
+
 pub fn event_type_in(
     name: &str,
     schema: impl Into<Option<serde_json::Value>>,
@@ -149,13 +186,14 @@ pub fn event_type_in(
 
 // Common tests
 pub async fn common_test_list<
-    ModelOut: DeserializeOwned + Clone + PartialEq + std::fmt::Debug,
+    ModelOut: DeserializeOwned + PartialEq + std::fmt::Debug,
     ModelIn: Serialize,
 >(
     client: &TestClient,
     path: &str,
     create_model: fn(usize) -> ModelIn,
     sort_asc: bool,
+    supports_reverse: bool,
 ) -> Result<()> {
     let mut items = Vec::new();
     for i in 0..10 {
@@ -168,7 +206,7 @@ pub async fn common_test_list<
         items.push(item);
     }
 
-    let list = run_with_retries(|| async {
+    let original_list = run_with_retries(|| async {
         let list = client
             .get::<ListResponse<ModelOut>>(&format!("{path}?with_content=true"), StatusCode::OK)
             .await
@@ -183,11 +221,11 @@ pub async fn common_test_list<
 
     if sort_asc {
         for i in 0..10 {
-            assert_eq!(items.get(i), list.data.get(i));
+            assert_eq!(items.get(i), original_list.data.get(i));
         }
     } else {
         for i in 0..10 {
-            assert_eq!(items.get(10 - i), list.data.get(i));
+            assert_eq!(items.get(9 - i), original_list.data.get(i));
         }
     }
 
@@ -235,6 +273,19 @@ pub async fn common_test_list<
     assert_eq!(list.data.len(), 4);
     assert!(list.done);
 
+    let prev = client
+        .get::<ListResponse<ModelOut>>(
+            &format!("{}?limit=3&iterator={}", path, list.prev_iterator.unwrap()),
+            StatusCode::OK,
+        )
+        .await
+        .unwrap();
+    assert_eq!(prev.data.len(), 3);
+    assert_eq!(
+        prev.data.first().unwrap(),
+        original_list.data.get(3).unwrap()
+    );
+
     let _list = client
         .get::<IgnoredResponse>(
             &format!("{path}?limit=6&iterator=BAD-$$$ITERATOR"),
@@ -243,6 +294,46 @@ pub async fn common_test_list<
         .await
         .unwrap();
 
+    if supports_reverse {
+        let opposite_order = if sort_asc { "descending" } else { "ascending" };
+
+        let opposite_1 = client
+            .get::<ListResponse<ModelOut>>(
+                &format!("{path}?limit=3&order={opposite_order}"),
+                StatusCode::OK,
+            )
+            .await
+            .unwrap();
+
+        let opposite_2 = client
+            .get::<ListResponse<ModelOut>>(
+                &format!(
+                    "{path}?limit=3&order={opposite_order}&iterator={}",
+                    opposite_1.iterator.unwrap()
+                ),
+                StatusCode::OK,
+            )
+            .await
+            .unwrap();
+
+        let opposite_prev = client
+            .get::<ListResponse<ModelOut>>(
+                &format!(
+                    "{path}?limit=3&order={opposite_order}&iterator={}",
+                    opposite_2.prev_iterator.unwrap()
+                ),
+                StatusCode::OK,
+            )
+            .await
+            .unwrap();
+
+        for i in 0..3 {
+            assert_eq!(original_list.data.get(9 - i), opposite_1.data.get(i));
+            assert_eq!(original_list.data.get(6 - i), opposite_2.data.get(i));
+            assert_eq!(original_list.data.get(9 - i), opposite_prev.data.get(i));
+        }
+    }
+
     // Test limits -- ten models were created previously and the default hard/soft cap is 250 so
     // 10..251 is the sane range here.
     for i in 10..251 {
diff --git a/server/svix-server/tests/utils/mod.rs b/server/svix-server/tests/utils/mod.rs
index b91fe0c44..fe722641f 100644
--- a/server/svix-server/tests/utils/mod.rs
+++ b/server/svix-server/tests/utils/mod.rs
@@ -8,6 +8,7 @@ use std::{
 };
 
 use anyhow::{Context, Result};
+use axum::response::IntoResponse;
 use reqwest::{Client, RequestBuilder, StatusCode};
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use tokio::sync::mpsc;
@@ -258,6 +259,7 @@ pub async fn start_svix_server_with_cfg_and_org_id(
     (TestClient::new(base_uri, &token), jh)
 }
 
+#[derive(Debug)]
 pub struct TestReceiver {
     pub endpoint: String,
     pub jh: tokio::task::JoinHandle<()>,
@@ -267,19 +269,27 @@ pub struct TestReceiver {
 }
 
 #[derive(Clone)]
-pub struct TestAppState {
+pub struct TestAppState<T: IntoResponse + Clone> {
     tx: mpsc::Sender<serde_json::Value>,
     header_tx: mpsc::Sender<HeaderMap>,
     response_status_code: Arc<Mutex<ResponseStatusCode>>,
+    response_body: T,
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct ResponseStatusCode {
     pub status_code: axum::http::StatusCode,
 }
 
 impl TestReceiver {
     pub fn start(resp_with: axum::http::StatusCode) -> Self {
+        Self::start_with_body(resp_with, ())
+    }
+
+    pub fn start_with_body<T>(resp_with: axum::http::StatusCode, body: T) -> Self
+    where
+        T: IntoResponse + Clone + Send + Sync + 'static,
+    {
         let listener = TcpListener::bind("127.0.0.1:0").unwrap();
         let endpoint = format!("http://{}/", listener.local_addr().unwrap());
 
@@ -299,6 +309,7 @@ impl TestReceiver {
                 tx,
                 header_tx,
                 response_status_code: response_status_code.clone(),
+                response_body: body,
             })
             .into_make_service();
 
@@ -324,18 +335,22 @@ impl TestReceiver {
     }
 }
 
-async fn test_receiver_route(
+async fn test_receiver_route<T: IntoResponse + Clone>(
     axum::extract::State(TestAppState {
         tx,
         header_tx,
         response_status_code,
-    }): axum::extract::State<TestAppState>,
+        response_body,
+    }): axum::extract::State<TestAppState<T>>,
     headers: HeaderMap,
     axum::Json(json): axum::Json<serde_json::Value>,
-) -> axum::http::StatusCode {
+) -> (axum::http::StatusCode, impl IntoResponse) {
     tx.send(json).await.unwrap();
     header_tx.send(headers).await.unwrap();
-    response_status_code.lock().unwrap().status_code
+    (
+        response_status_code.lock().unwrap().status_code,
+        response_body,
+    )
 }
 
 pub async fn run_with_retries<O, F, C>(f: C) -> Result<O>