diff --git a/README.md b/README.md index 91ec599b2..b5978eb84 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ See the [Outpost Features](https://outpost.hookdeck.com/docs/features) for more - [Quickstarts](https://outpost.hookdeck.com/docs/quickstarts) - [Features](https://outpost.hookdeck.com/docs/features) - [Guides](https://outpost.hookdeck.com/docs/guides) -- [API Reference](https://outpost.hookdeck.com/docs/references/api) +- [API Reference](https://outpost.hookdeck.com/docs/api) - [Configuration Reference](https://outpost.hookdeck.com/docs/references/configuration) _The Outpost documentation is built using the [Zudoku documentation framework](https://zuplo.link/outpost)._ @@ -180,7 +180,7 @@ Create a tenant with the following command, replacing `$TENANT_ID` with a unique ```sh -curl --location --request PUT "$OUTPOST_URL/api/v1/$TENANT_ID" \ +curl --location --request PUT "$OUTPOST_URL/api/v1/tenants/$TENANT_ID" \ --header "Authorization: Bearer $API_KEY" ``` @@ -189,7 +189,7 @@ Run a local server exposed via a localtunnel or use a hosted service such as the Create a webhook destination where events will be delivered to with the following command. Again, replace `$TENANT_ID` and `$API_KEY`. Also, replace `$URL` with the webhook destinations URL: ```sh -curl --location "$OUTPOST_URL/api/v1/$TENANT_ID/destinations" \ +curl --location "$OUTPOST_URL/api/v1/tenants/$TENANT_ID/destinations" \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ @@ -225,7 +225,7 @@ Check the logs on your server or your webhook capture tool for the delivered eve Get an Outpost portal link for the tenant: ```sh -curl "$OUTPOST_URL/api/v1/$TENANT_ID/portal" \ +curl "$OUTPOST_URL/api/v1/tenants/$TENANT_ID/portal" \ --header "Authorization: Bearer $API_KEY" ``` @@ -241,7 +241,7 @@ Open the `redirect_url` link to view the Outpost portal. ![Dashboard homepage](docs/public/images/dashboard-homepage.png) -Continue to use the [Outpost API](https://outpost.hookdeck.com/docs/references/api) or the Outpost portal to add and test more destinations. +Continue to use the [Outpost API](https://outpost.hookdeck.com/docs/api) or the Outpost portal to add and test more destinations. ## Contributing diff --git a/contributing/step-by-step.md b/contributing/step-by-step.md index e5698a619..4604ed760 100644 --- a/contributing/step-by-step.md +++ b/contributing/step-by-step.md @@ -82,7 +82,7 @@ OK% 1: Create a tenant with ID: 123 ```sh -$ curl --location --request PUT 'localhost:3333/api/v1/123' \ +$ curl --location --request PUT 'localhost:3333/api/v1/tenants/123' \ --header 'Authorization: Bearer apikey' {"id":"123","created_at":"..."}% ``` @@ -90,7 +90,7 @@ $ curl --location --request PUT 'localhost:3333/api/v1/123' \ 2: Create a webhook-type destination ```sh -$ curl --location 'localhost:3333/api/v1/123/destinations' \ +$ curl --location 'localhost:3333/api/v1/tenants/123/destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer apikey' \ --data '{ @@ -108,7 +108,7 @@ $ curl --location 'localhost:3333/api/v1/123/destinations' \ Feel free to confirm that the destination is successfully created either in Redis or by listing the destination for tenant 123: ```sh -$ curl --location 'localhost:3333/api/v1/123/destinations' \ +$ curl --location 'localhost:3333/api/v1/tenants/123/destinations' \ --header 'Authorization: Bearer apikey' ``` @@ -149,7 +149,7 @@ $ go run ./cmd/destinations/webhooks You can also confirm the data by checking the data in ClickHouse or querying the Event API: ```sh -$ curl --location 'localhost:3333/api/v1/123/events' \ +$ curl --location 'localhost:3333/api/v1/tenants/123/events' \ --header 'Authorization: Bearer apikey' ``` @@ -160,7 +160,7 @@ $ curl --location 'localhost:3333/api/v1/123/events' \ To get the portal URL: ```sh -$ curl --location 'localhost:3333/api/v1/123/portal' \ +$ curl --location 'localhost:3333/api/v1/tenants/123/portal' \ --header 'Authorization: Bearer apikey' {"redirect_url":"http://localhost:3333?token=eyJH..."}% ``` diff --git a/docs/pages/destinations.mdx b/docs/pages/destinations.mdx index c5aaf2c51..a902f88ec 100644 --- a/docs/pages/destinations.mdx +++ b/docs/pages/destinations.mdx @@ -26,7 +26,7 @@ See the [roadmap](/docs/references/roadmap) for planned destination types. To be Destinations can be registered through the tenant portal or via the API. Each destination type has its own configuration and credentials. Refer to the [Create Destination API](/docs/api/destinations#create-destination) for the required `config` and `credentials` fields for each destination type. ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/aws-kinesis.mdx b/docs/pages/destinations/aws-kinesis.mdx index 7ea72a76e..28205f238 100644 --- a/docs/pages/destinations/aws-kinesis.mdx +++ b/docs/pages/destinations/aws-kinesis.mdx @@ -26,7 +26,7 @@ Stream events to an Amazon Kinesis Data Stream. ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/aws-s3.mdx b/docs/pages/destinations/aws-s3.mdx index 94d18d4db..7641c0a84 100644 --- a/docs/pages/destinations/aws-s3.mdx +++ b/docs/pages/destinations/aws-s3.mdx @@ -27,7 +27,7 @@ Store events in an Amazon S3 bucket. ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/aws-sqs.mdx b/docs/pages/destinations/aws-sqs.mdx index c1831d0cc..e7a01a535 100644 --- a/docs/pages/destinations/aws-sqs.mdx +++ b/docs/pages/destinations/aws-sqs.mdx @@ -24,7 +24,7 @@ Send events to an Amazon SQS queue. ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/azure-service-bus.mdx b/docs/pages/destinations/azure-service-bus.mdx index 53b9bed18..ec70f42c7 100644 --- a/docs/pages/destinations/azure-service-bus.mdx +++ b/docs/pages/destinations/azure-service-bus.mdx @@ -21,7 +21,7 @@ Send events to an Azure Service Bus queue or topic. ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/gcp-pubsub.mdx b/docs/pages/destinations/gcp-pubsub.mdx index d7be053aa..09306fcb9 100644 --- a/docs/pages/destinations/gcp-pubsub.mdx +++ b/docs/pages/destinations/gcp-pubsub.mdx @@ -25,7 +25,7 @@ Publish events to a Google Cloud Pub/Sub topic. ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/hookdeck.mdx b/docs/pages/destinations/hookdeck.mdx index ce3758974..b71af6e09 100644 --- a/docs/pages/destinations/hookdeck.mdx +++ b/docs/pages/destinations/hookdeck.mdx @@ -25,7 +25,7 @@ This destination has no configuration fields. Routing and delivery settings are ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/rabbitmq.mdx b/docs/pages/destinations/rabbitmq.mdx index 854f94d46..039270abf 100644 --- a/docs/pages/destinations/rabbitmq.mdx +++ b/docs/pages/destinations/rabbitmq.mdx @@ -24,7 +24,7 @@ Send events to a RabbitMQ exchange via AMQP. ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/destinations/webhook.mdx b/docs/pages/destinations/webhook.mdx index 4e8b0b68d..e719cec6e 100644 --- a/docs/pages/destinations/webhook.mdx +++ b/docs/pages/destinations/webhook.mdx @@ -66,7 +66,7 @@ If `secret` is not provided, one will be auto-generated. Tenants cannot modify s ### Example ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ @@ -85,7 +85,7 @@ Secret rotation allows you to change the signing secret without downtime. During To rotate a secret, set `rotate_secret` to `true` when updating a destination: ```sh -curl --location --request PATCH 'https:///api/v1//destinations/' \ +curl --location --request PATCH 'https:///api/v1/tenants//destinations/' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ @@ -104,7 +104,7 @@ When rotation is triggered: To customize the rotation window, set `previous_secret_invalid_at`: ```sh -curl --location --request PATCH 'https:///api/v1//destinations/' \ +curl --location --request PATCH 'https:///api/v1/tenants//destinations/' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ @@ -228,7 +228,7 @@ Standard Webhooks secrets use the format `whsec_`, which is automaticall Tenants can add custom HTTP headers to webhook requests. This is useful for authentication, routing, or passing additional metadata to their endpoint. ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/features/filter.mdx b/docs/pages/features/filter.mdx index 489e5ac35..3fb56e458 100644 --- a/docs/pages/features/filter.mdx +++ b/docs/pages/features/filter.mdx @@ -273,7 +273,7 @@ Process only high-value orders: Filters are set when creating or updating a destination via the `filter` field: ```sh -curl --location 'https:///api/v1//destinations' \ +curl --location 'https:///api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ @@ -293,7 +293,7 @@ curl --location 'https:///api/v1//destinations' \ To remove a filter from a destination, set the `filter` field to an empty object: ```sh -curl --location --request PATCH 'https:///api/v1//destinations/' \ +curl --location --request PATCH 'https:///api/v1/tenants//destinations/' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/features/multi-tenant-support.mdx b/docs/pages/features/multi-tenant-support.mdx index c40497818..16776b66d 100644 --- a/docs/pages/features/multi-tenant-support.mdx +++ b/docs/pages/features/multi-tenant-support.mdx @@ -15,7 +15,7 @@ View the Tenant API docs [here](/docs/api/tenants). Create a tenant with the following command, replacing `` with a unique identifier such as "your_org_name", and the `` with the value you set in your Outpost installation `API_KEY` environment variable: ```sh -curl --location --request PUT 'localhost:3333/api/v1/' \ +curl --location --request PUT 'localhost:3333/api/v1/tenants/' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' ``` diff --git a/docs/pages/features/topics.mdx b/docs/pages/features/topics.mdx index 136ede859..26c2415cf 100644 --- a/docs/pages/features/topics.mdx +++ b/docs/pages/features/topics.mdx @@ -23,7 +23,7 @@ Events published for a given tenant are evaluated against the destination's topi Create destinations with the following request, replacing `` and ``. The `` must be one of the supported [event destination types](#event-destination-types), and the `` contents will depend on the event destination type. ```sh -curl --location 'localhost:3333/api/v1//destinations' \ +curl --location 'localhost:3333/api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ @@ -36,7 +36,7 @@ curl --location 'localhost:3333/api/v1//destinations' \ For example, the following creates a webhook destination that subscribes to all topics (`*`) and delivers published events to the URL `https://example.test/webhooks`: ```sh -curl --location 'localhost:3333/api/v1//destinations' \ +curl --location 'localhost:3333/api/v1/tenants//destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ diff --git a/docs/pages/guides.mdx b/docs/pages/guides.mdx index e01a3b64f..dd26a2734 100644 --- a/docs/pages/guides.mdx +++ b/docs/pages/guides.mdx @@ -20,6 +20,7 @@ Welcome to the Outpost guides section. These guides will help you get the most o - [Migrate Existing Webhooks to Outpost](/guides/migrate-to-outpost) - [Schema Migration](/guides/migration) +- [Upgrade to v0.12](/guides/upgrade-v0.12) ## Next Steps diff --git a/docs/pages/guides/building-your-own-ui.mdx b/docs/pages/guides/building-your-own-ui.mdx index 680836688..0ab3076a0 100644 --- a/docs/pages/guides/building-your-own-ui.mdx +++ b/docs/pages/guides/building-your-own-ui.mdx @@ -21,7 +21,7 @@ Proxying through your own API can be useful if you want to limit access to some You can generate a JWT token by using the [Tenant JWT Token API](/docs/api/tenants#get-tenant-jwt-token). ```bash -curl --location 'localhost:3333/api/v1//token' \ +curl --location 'localhost:3333/api/v1/tenants//token' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ ``` @@ -43,7 +43,7 @@ const [destination_types, setDestinationTypes] = useState([]); const fetchDestinations = async () => { // Get the tenant destinations - const response = await fetch(`${API_URL}/api/v1/destinations`, { + const response = await fetch(`${API_URL}/api/v1/tenants/destinations`, { headers: { Authorization: `Bearer ${token}`, }, @@ -341,7 +341,7 @@ Events are listed using the [List Events API](/docs/api/events#list-events). You const [events, setEvents] = useState([]); const fetchEvents = async () => { - const response = await fetch(`${API_URL}/api/v1/events`, { + const response = await fetch(`${API_URL}/api/v1/tenants/events`, { headers: { Authorization: `Bearer ${token}`, }, diff --git a/docs/pages/guides/publish-from-gcp-pubsub.mdx b/docs/pages/guides/publish-from-gcp-pubsub.mdx index da9c16226..c0a522e47 100644 --- a/docs/pages/guides/publish-from-gcp-pubsub.mdx +++ b/docs/pages/guides/publish-from-gcp-pubsub.mdx @@ -6,7 +6,7 @@ This guide provides information on using GCP Pub/Sub to publish events to Outpos ## Message Structure -GCP Pub/Sub messages should have the same payload structure as the [Publish API endpoint](/docs/references/api#publish). +GCP Pub/Sub messages should have the same payload structure as the [Publish API endpoint](/docs/api/publish). ```json { diff --git a/docs/pages/guides/publish-from-rabbitmq.mdx b/docs/pages/guides/publish-from-rabbitmq.mdx index 0284a19dd..532585a0b 100644 --- a/docs/pages/guides/publish-from-rabbitmq.mdx +++ b/docs/pages/guides/publish-from-rabbitmq.mdx @@ -6,7 +6,7 @@ This guide provides information on using RabbitMQ to publish events to Outpost. ## Message Structure -RabbitMQ messages should have the same payload structure as the [Publish API endpoint](/docs/references/api#publish). +RabbitMQ messages should have the same payload structure as the [Publish API endpoint](/docs/api/publish). ```json { diff --git a/docs/pages/guides/publish-from-sqs.mdx b/docs/pages/guides/publish-from-sqs.mdx index 5344ecbb5..9dbb1a569 100644 --- a/docs/pages/guides/publish-from-sqs.mdx +++ b/docs/pages/guides/publish-from-sqs.mdx @@ -6,7 +6,7 @@ This guide provides information on using SQS to publish events to Outpost. ## Message Structure -SQS messages should have the same payload structure as the [Publish API endpoint](/docs/references/api#publish). +SQS messages should have the same payload structure as the [Publish API endpoint](/docs/api/publish). ```json { diff --git a/docs/pages/guides/upgrade-v0.12.mdx b/docs/pages/guides/upgrade-v0.12.mdx new file mode 100644 index 000000000..2d8f53c9d --- /dev/null +++ b/docs/pages/guides/upgrade-v0.12.mdx @@ -0,0 +1,109 @@ +--- +title: "Upgrade to v0.12" +--- + +This guide covers breaking changes and migration steps when upgrading from v0.11 to v0.12. + +## Breaking Changes Overview + +| Change | Impact | Action Required | +|--------|--------|-----------------| +| [API route prefix](#api-route-prefix) | All tenant-scoped endpoints | Update API client paths | +| [Webhook signature defaults](#webhook-signature-defaults) | Signature verification | Update receivers or configure for backward compatibility | +| [List Tenants & Redis migrations](#list-tenants--redis-migrations) | New feature + data format | Run migrations | + +## API Route Prefix + +All tenant-scoped API endpoints now use a `/tenants/` prefix. + +| Old Path | New Path | +|----------|----------| +| `PUT /api/v1/:tenant_id` | `PUT /api/v1/tenants/:tenant_id` | +| `GET /api/v1/:tenant_id` | `GET /api/v1/tenants/:tenant_id` | +| `DELETE /api/v1/:tenant_id` | `DELETE /api/v1/tenants/:tenant_id` | +| `GET /api/v1/:tenant_id/destinations` | `GET /api/v1/tenants/:tenant_id/destinations` | +| `POST /api/v1/:tenant_id/destinations` | `POST /api/v1/tenants/:tenant_id/destinations` | +| `GET /api/v1/:tenant_id/events` | `GET /api/v1/tenants/:tenant_id/events` | +| ... | ... | + +The pattern applies to all endpoints that previously started with `/:tenant_id`. + +**Action:** Update your API clients to use the new `/tenants/` prefixed paths before upgrading. + +## Webhook Signature Defaults + +The default webhook signature templates have changed to a simpler format without timestamps. + +| Setting | Old Default (v0.11) | New Default (v0.12) | +|---------|---------------------|---------------------| +| `DESTINATIONS_WEBHOOK_SIGNATURE_CONTENT_TEMPLATE` | `{{.Timestamp.Unix}}.{{.Body}}` | `{{.Body}}` | +| `DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATE` | `t={{.Timestamp.Unix}},v0={{.Signatures \| join ","}}` | `v0={{.Signatures \| join ","}}` | + +If your webhook receivers verify signatures using the old format, signature verification will fail after upgrading. + +**To maintain backward compatibility**, set these environment variables: + +```bash +DESTINATIONS_WEBHOOK_SIGNATURE_CONTENT_TEMPLATE="{{.Timestamp.Unix}}.{{.Body}}" +DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATE="t={{.Timestamp.Unix}},v0={{.Signatures | join \",\"}}" +``` + +## List Tenants & Redis Migrations + +v0.12 introduces a new `GET /api/v1/tenants` endpoint for listing all tenants with cursor-based pagination. This feature requires Redis migrations to be run. + +:::note[RediSearch Required] +This endpoint requires Redis with the RediSearch module (e.g., `redis/redis-stack-server`). If RediSearch is not available, the endpoint returns `501 Not Implemented`. +::: + +See the [API Reference](/docs/api/tenants#list-tenants) for details. + +### Migrations Included + +- **002_timestamps**: Converts timestamp fields to Unix format for timezone-agnostic sorting +- **003_entity**: Creates RediSearch indexes for tenant listing + +### Running Migrations + +Migrations run automatically at startup. For production environments, manual migration is recommended. + +:::note +These commands require environment variables for Redis connection (`REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, etc.). You can also use `--config` to pass a config file. See the [Schema Migration Guide](/guides/migration#configuration) for details. +::: + +```bash +# Preview changes +docker run --rm -e REDIS_HOST=... -e REDIS_PASSWORD=... hookdeck/outpost:v0.12.0 migrate plan + +# Apply migrations +docker run --rm -it -e REDIS_HOST=... -e REDIS_PASSWORD=... hookdeck/outpost:v0.12.0 migrate apply + +# Verify migrations +docker run --rm -e REDIS_HOST=... -e REDIS_PASSWORD=... hookdeck/outpost:v0.12.0 migrate verify +``` + +For systems with high write throughput, re-run migrations after the initial upgrade: + +```bash +docker run --rm -it -e REDIS_HOST=... -e REDIS_PASSWORD=... hookdeck/outpost:v0.12.0 migrate apply --rerun +``` + +:::tip[Migration Tool Reference] +For detailed information on the migration workflow, safety features, and troubleshooting, see the [Schema Migration Guide](/guides/migration). +::: + +## Upgrade Checklist + +1. **Before upgrading:** + - [ ] Update API clients to use `/tenants/` prefix paths + - [ ] Decide on webhook signature format (keep old or adopt new) + - [ ] Back up Redis data + +2. **During upgrade:** + - [ ] Set webhook signature environment variables if maintaining backward compatibility + - [ ] Run migrations (automatic or manual) + +3. **After upgrading:** + - [ ] Verify migrations completed: `migrate verify` + - [ ] For high-volume systems: `migrate apply --rerun` + - [ ] Test webhook signature verification diff --git a/docs/pages/overview.mdx b/docs/pages/overview.mdx index e687b718d..f85279646 100644 --- a/docs/pages/overview.mdx +++ b/docs/pages/overview.mdx @@ -76,7 +76,7 @@ Outpost supports features required to provide a best-in-class event destinations ## References -- [API Reference](/docs/references/api) +- [API Reference](/docs/api) - [Configuration Reference](/docs/references/configuration) - [Roadmap](/docs/references/roadmap) diff --git a/docs/pages/quickstarts/docker.mdx b/docs/pages/quickstarts/docker.mdx index f69516dc6..14c12db25 100644 --- a/docs/pages/quickstarts/docker.mdx +++ b/docs/pages/quickstarts/docker.mdx @@ -74,7 +74,7 @@ URL=your_webhook_url 2. Create a tenant with the following command, replacing `$TENANT_ID` with a unique identifier such as "your_org_name", and the `$API_KEY` with the value you set in your `.env`: ```sh - curl --location --request PUT "localhost:3333/api/v1/$TENANT_ID" \ + curl --location --request PUT "localhost:3333/api/v1/tenants/$TENANT_ID" \ --header "Authorization: Bearer $API_KEY" ``` @@ -83,7 +83,7 @@ URL=your_webhook_url 4. Create a webhook destination where events will be delivered to using the following command. Again, replace `$TENANT_ID` and `$API_KEY`. Also, replace `$URL` with the webhook destination's URL: ```sh - curl --location "localhost:3333/api/v1/$TENANT_ID/destinations" \ + curl --location "localhost:3333/api/v1/tenants/$TENANT_ID/destinations" \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ @@ -119,7 +119,7 @@ URL=your_webhook_url 7. Get an Outpost portal link for the tenant: ```sh - curl "localhost:3333/api/v1/$TENANT_ID/portal" \ + curl "localhost:3333/api/v1/tenants/$TENANT_ID/portal" \ --header "Authorization: Bearer $API_KEY" ``` @@ -135,4 +135,4 @@ URL=your_webhook_url ![Outpost portal home page](../../public/images/dashboard-homepage.png) -Continue to use the [Outpost API](https://outpost.hookdeck.com/docs/references/api) or the Outpost portal to add and test more destinations. +Continue to use the [Outpost API](https://outpost.hookdeck.com/docs/api) or the Outpost portal to add and test more destinations. diff --git a/docs/pages/quickstarts/railway.mdx b/docs/pages/quickstarts/railway.mdx index 3fb01383e..911f415b8 100644 --- a/docs/pages/quickstarts/railway.mdx +++ b/docs/pages/quickstarts/railway.mdx @@ -50,7 +50,7 @@ URL=your_webhook_url 2. Create a tenant with the following command, replacing `$TENANT_ID` with a unique identifier such as "your_org_name", and the `$API_KEY` with the value from your Railway environment variables: ```sh - curl --location --request PUT "$OUTPOST_URL/api/v1/$TENANT_ID" \ + curl --location --request PUT "$OUTPOST_URL/api/v1/tenants/$TENANT_ID" \ --header "Authorization: Bearer $API_KEY" ``` @@ -59,7 +59,7 @@ URL=your_webhook_url 4. Create a webhook destination where events will be delivered to using the following command. Again, replace `$TENANT_ID` and `$API_KEY`. Also, replace `$URL` with the webhook destination's URL: ```sh - curl --location "$OUTPOST_URL/api/v1/$TENANT_ID/destinations" \ + curl --location "$OUTPOST_URL/api/v1/tenants/$TENANT_ID/destinations" \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ @@ -95,7 +95,7 @@ URL=your_webhook_url 7. Get an Outpost portal link for the tenant: ```sh - curl "$OUTPOST_URL/api/v1/$TENANT_ID/portal" \ + curl "$OUTPOST_URL/api/v1/tenants/$TENANT_ID/portal" \ --header "Authorization: Bearer $API_KEY" ``` @@ -111,4 +111,4 @@ URL=your_webhook_url ![Outpost portal home page](../../public/images/dashboard-homepage.png) -Continue to use the [Outpost API](https://outpost.hookdeck.com/docs/references/api) or the Outpost portal to add and test more destinations. +Continue to use the [Outpost API](https://outpost.hookdeck.com/docs/api) or the Outpost portal to add and test more destinations. diff --git a/docs/pages/references.mdx b/docs/pages/references.mdx index fd2a78a60..2db39ec73 100644 --- a/docs/pages/references.mdx +++ b/docs/pages/references.mdx @@ -6,7 +6,7 @@ Here you'll find references for Outpost global configuration, planning, roadmap - [Outpost Configuration](/docs/references/configuration) - [Outpost Roadmap](/docs/references/roadmap) -- [Outpost API](/docs/api/authentication) +- [Outpost API](/docs/api) ## External Links diff --git a/docs/pages/references/api.mdx b/docs/pages/references/api.mdx deleted file mode 100644 index 1b7c35cbb..000000000 --- a/docs/pages/references/api.mdx +++ /dev/null @@ -1,636 +0,0 @@ ---- -title: "Outpost API Reference" ---- - -The API is a REST-based JSON API that can be publicly exposed or only available on your internal network / VPC, depending on your network topology. - -## Authentication - -### Admin API Key - -The API uses bearer token authentication with a token of your choice configured through the `API_KEY` environment variable. - -### JWT Token - -The API can also be authenticated using a per tenant token that’s valid for 24 hours. When using the a JWT token the `/:tenant_id` of each API endpoints is inferred to be the authenticated token and is not necessary. - -## Tenants - -The API segments resources per `tenant`. A tenant represents a user/team/organization in your product. The provided value determines the tenant's ID, which can be any string representation. - -If your system is not multi-tenant, create a single tenant with a hard-code tenant ID upon initialization. If your system has a single tenant but multiple environments, create a tenant per environment, like `live` and `test`. - -### Tenant Object - -```json -{ - "id": "123", //User-defined system ID - "destinations_count": 5, // Integer - "topics": ["user.created", "user.deleted"], // List of subscribed topics across all destinations - "created_at": "2024-01-01T00:00:00Z" // ISO Date -} -``` - -### `GET` `/tenants` - -List all tenants with cursor-based pagination. - -:::note[RediSearch Required] -This endpoint requires Redis with the RediSearch module (e.g., `redis/redis-stack-server`). If RediSearch is not available, this endpoint returns `501 Not Implemented`. Outpost automatically detects RediSearch availability at startup. -::: - -The response includes lightweight tenant objects without computed fields like `destinations_count` and `topics`. Use `GET /:tenant_id` to retrieve full tenant details. - -#### Query Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `limit` | integer | 20 | Number of tenants per page (1-100) | -| `order` | string | `desc` | Sort order by `created_at`: `asc` or `desc` | -| `next` | string | - | Cursor for next page (mutually exclusive with `prev`) | -| `prev` | string | - | Cursor for previous page (mutually exclusive with `next`) | - -#### Response - -```json -{ - "data": [ - { - "id": "tenant_123", - "metadata": { "plan": "pro" }, - "created_at": "2024-01-15T10:30:00Z", - "updated_at": "2024-01-15T10:30:00Z" - }, - { - "id": "tenant_456", - "metadata": null, - "created_at": "2024-01-14T09:00:00Z", - "updated_at": "2024-01-14T09:00:00Z" - } - ], - "next": "MTcwNDA2NzIwMA==", - "prev": null -} -``` - -#### Error Responses - -- `400 Bad Request` - Invalid cursor or conflicting pagination parameters -- `501 Not Implemented` - RediSearch not available - -### `PUT` `/:tenant_id` - -Idempotently creates the tenant. A tenant is required to associate a destination. - -#### Request - -Empty body - -#### Response - -```json -{ - "id": "123", - "destinations_count": 5, - "topics": ["user.created", "user.deleted"], - "created_at": "2024-01-01T00:00:00Z" -} -``` - -### `GET` `/:tenant_id` - -Get a tenant. - -#### Response - -```json -{ - "id": "123", - "destinations_count": 5, - "topics": ["user.created", "user.deleted"], - "created_at": "2024-01-01T00:00:00Z" -} -``` - -### `GET` `/:tenant_id/portal` - -Return a redirect URL for a user portal URL. The redirect URL contains a JWT that authenticates the user with the portal. - -#### Request - -Supported parameters: - -- `?theme=(light|dark)` Optional query param to force a specific theme to display the portal in to match the current theme of your own dashboard - -#### Response - -```json -{ - "redirect_url": "https://webhooks.acme.com?token=JWT_TOKEN" // Redirect URL with authentication token -} -``` - -### `GET` `/:tenant_id/token` - -Return a JTW token to authenticate the user with the API scoped to their specific tenant in order to call the API from the browser safely. - -#### Response - -```json -{ - "token": "SOME_JWT_TOKEN" -} -``` - -### `DELETE` `/:tenant_id` - -Delete the tenant and all associated destinations - -#### Request - -Empty body - -#### Response - -```json -{ - "success": true -} -``` - -## Destinations - -### Destination Object - -```json -{ - "id": "des_12345", // Control plane generated ID or user provided ID - "type": "webhooks", // Type of the destination - "topics": ["user.created", "user.updated"], // Topics of events this destination is eligible for - "config": { - // Destination type specific configuration. Schema of depends on type - "url": "https://example.com/webhooks/user" - }, - "credentials": { - // Destination type specific credentials. AES encrypted. Schema depends on type - "secret": "some***********" - }, - "delivery_metadata": { - // Optional. Static key-value pairs automatically merged into event metadata on every delivery - "x-api-key": "sk_live_abc123", - "x-tenant-id": "tenant_456" - }, - "metadata": { - // Optional. Arbitrary contextual information stored with the destination - "name": "Production Webhook Server", - "description": "Main webhook endpoint for order processing" - }, - "disabled_at": null, // null or ISO date if disabled - "created_at": "2024-01-01T00:00:00Z" // Date the destination was created -} -``` - -#### Field Details - -- **`topics`**: Can contain either a list of topics or a wildcard `*` implying that all topics are supported. If you do not wish to implement topics for your application, set all destination topics to `*`. - -- **`credentials`** (optional): By default all destination credentials are obfuscated and the values cannot be read. This does not apply to the `webhook` type destination secret—each destination type can expose their own obfuscation logic. - -- **`delivery_metadata`** (optional): Static key-value pairs automatically merged into event metadata on every delivery. Values are encrypted like credentials. Useful for destination-specific metadata like API keys, authentication tokens, tenant identifiers, or any custom headers/attributes required by the receiving service. For webhook destinations, these are translated to HTTP headers. The merge priority is: system metadata (event-id, topic, timestamp) < delivery_metadata < event metadata. - -- **`metadata`** (optional): Arbitrary contextual information stored with the destination for organizational purposes (e.g., name, description, notes). Not encrypted and not included in event deliveries. Useful for documenting what a destination is used for or adding labels for filtering. - -### `GET` `/:tenant_id/destinations` - -Return a list of the destinations. The endpoint is not paged, and the maximum number is equivalent to the maximum configured number of destinations per tenant through the `MAX_DESTINATIONS_PER_TENANT` env variable. - -#### Request - -Supported Parameters: - -- `type` `string | string[]` The type of destinations to return -- `topics` `string | string[]` Return destinations for a specific event type - -#### Response - -```json -[ - { - "id": "dest_123456", // Control plane generated ID or user provided ID - "type": "webhooks", // Type of the destination - "topics": ["user.created", "user.updated"], // Type of events this destination is eligible for - "config": { - // Destination type specific configuration. Schema of depends on type - "url": "https://example.com/webhooks/user" - }, - "credentials": { - // Destination type specific credentials. AES encrypted. Schema depends on type - "secret": "something-super-secret" - }, - "created_at": "2024-01-01T00:00:00Z" // Date the destination was created - } -] -``` - -### `POST` `/:tenant_id/destinations` - -Create a new destination type with the provided configuration. - -#### Request - -```json -{ - "id": "123", // Optional, UUID will be generated if empty - "type": "webhooks", // String of valid destination type - "topics": "*", // '*' or String[] of enabled topics, "*" for all - "config": { - // Config object for the given type - "url": "https://example.com/webhooks" - }, - "credentials": null // Credentials for the given type. It can be empty or null. -} -``` - -#### Response - -```json -{ - "id": "des_12345", // ID of the destination - "type": "webhooks", // Type of the destination - "topics": ["*"], // Topics of events this destination is eligible for - "config": { - // Destination type specific configuration. Schema of depends on type - "url": "https://example.com/webhooks" - }, - "credentials": { - // Destination type specific credentials. AES encrypted. Schema depends on type - "secret": "some************" - }, - "disabled_at": null, // null or ISO date if disabled - "created_at": "2024-01-01T00:00:00Z" // Date the destination was created -} -``` - -#### Webhook secret & rotation - -Webhook secrets and rotations are a special case for the `webhook` destination type. The destination type `credentials.secret` is only a valid input when using the Admin API and can be omited when a secret is generated automatically. - -Additionally, `credentials.previous_secret` can be used to set a rotated secret during a migration. `credentials.rotate_secret` can be used to automatically rotate the existing secret which results in a new `credentials.secret` and `credentials.previous_secret` value. - -`previous_secret` are valid and used to “double” sign the request for 24 hours. - -### `PATCH` `/:tenant_id/destinations/:destination_id` - -Update the destination configuration. - -#### Request - -```json -{ - "type": "webhooks", // Optional. String of valid destination type - "topics": "*", // Optional. String[] of enabled topics, "*" for all - "config": { - // Optional. Config object for the given type - "url": "https://example.com/webhooks" - }, - "credentials": null // Optional. Credentials for the given type. It can be empty or null. -} -``` - -#### Response - -```json -{ - "id": "des_12345", // Control plane generated ID - "type": "webhooks", // Type of the destination - "topics": "*", // Topics of events this destination is eligible for - "config": { - // Destination type specific configuration. Schema of depends on type - "url": "https://example.com/webhooks" - }, - "credentials": { - // Destination type specific credentials. AES encrypted. Schema depends on type - "secret": "some********" - }, - "disabled_at": null, // null or ISO date if disabled - "created_at": "2024-01-01T00:00:00Z" // Date the destination was created -} -``` - -If the destination uses OAuth: - -```json -{ - "redirect_url": "https://dashboard.hookdeck.com/authorize?token=12313123" -} -``` - -### `PUT` `/:tenant_id/destinations/:destination_id/enable` - -Enable a previously disabled destination. - -#### Request - -Empty body - -#### Response - -```json -{ - "id": "des_12345", // Control plane generated ID - "type": "webhooks", // Type of the destination - "topics": "*", // Topics of events this destination is eligible for - "config": { - // Destination type specific configuration. Schema of depends on type - "url": "https://example.com/webhooks" - }, - "credentials": { - // Destination type specific credentials. AES encrypted. Schema depends on type - "secret": "some************" - }, - "disabled_at": null, // null or ISO date if disabled - "created_at": "2024-01-01T00:00:00Z" // Date the destination was created -} -``` - -### `PUT` `/:tenant_id/destinations/:destination_id/disable` - -Disable a previously enabled destination. - -#### Request - -Empty body - -#### Response - -```json -{ - "id": "des_12345", // Control plane generated ID - "type": "webhooks", // Type of the destination - "topics": "*", // Topics of events this destination is eligible for - "config": { - // Destination type specific configuration. Schema of depends on type - "url": "https://example.com/webhooks" - }, - "credentials": { - // Destination type specific credentials. AES encrypted. Schema depends on type - "secret": "some***********" - }, - "disabled_at": "2024-01-01T00:00:00Z", // null or ISO date if disabled - "created_at": "2024-01-01T00:00:00Z" // Date the destination was created -} -``` - -### `DELETE` `/:tenant_id/destinations/:destination_id` - -Delete the destination. - -#### Request - -Empty body - -#### Response - -```json -{ - "success": true -} -``` - -## Publish - -Publish events on one or more topics. - -### `POST` `/api/v1/publish` - -Publish an event. - -#### Request - -```json -{ - "tenant_id": "", - "destination_id": "", // Optional. Provide a way of routing events to a specific destination - "topic": "topic.name", // Topic defined in TOPICS environment variable - "eligible_for_retry": true | false, // Should event delivery be retried - "metadata": Payload, // can by any JSON payload, - "data": Payload // can by any JSON payload -} -``` - -#### Response - -```json -{ - "id": "evt_abc123xyz789", - "duplicate": false -} -``` - -- `id`: The ID of the event (provided in request or server-generated) -- `duplicate`: Whether this event was already processed (idempotency hit) - -#### Example - -```sh -curl --location 'localhost:3333/api/v1/publish' \ ---header 'Content-Type: application/json' \ ---header 'Authorization: Bearer ' \ ---data '{ - "tenant_id": "", - "topic": "user.created", - "eligible_for_retry": true, - "metadata": { - "meta": "data" - }, - "data": { - "user_id": "userid" - } -}' -``` - -## Schemas - -### `GET` `/destination-types` - -Runs a list of JSON-based input schemas for each destination type. - -#### Request - -No parameters. - -#### Response - -```json -[ - { - "type": "webhook", - "label": "Webhook", - "description": "Send event via an HTTP POST request to a URL", - "icon": "", - "instructions": "Some *markdown*", - "config_fields": [ - { - "type": "text", - "label": "URL", - "description": "The URL to send the event to", - "validation": "/((([A-Za-z]{3,9}:(?://)?)(?:[-;:&=+$,w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:www.|[-;:&=+$,w]+@)[A-Za-z0-9.-]+)((?:/[+~%/.w-_]*)???(?:[-+=&;%@.w_]*)#?(?:[w]*))?)/", - "required": true - } - ], - "credential_fields": [] - }, - { - "type": "hookdeck", - "label": "Hookdeck Event Gateway", - "description": "Send event to Hookdeck Event Gateway", - "icon": "", - "instructions": "Some *markdown*", - "remote_setup_url": "https://dashboard.hookdeck.com/authorize?provider=acme", - "config_fields": [], - "credential_fields": [ - { - "type": "text", - "label": "Publishing Key", - "description": "Your Hookdeck source publishing key", - "required": true - } - ] - } -] -``` - -### `GET` `/destination-types/:type` - -Runs the input schemas for the specified destination type. - -#### Response - -```json -{ - "type": "webhook", - "label": "Webhook", - "description": "Send event via an HTTP POST request to a URL", - "icon": "", - "instructions": "Some *markdown*", - "config_fields": [ - { - "type": "text", - "label": "URL", - "description": "The URL to send the event to", - "validation": "/((([A-Za-z]{3,9}:(?://)?)(?:[-;:&=+$,w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:www.|[-;:&=+$,w]+@)[A-Za-z0-9.-]+)((?:/[+~%/.w-_]*)???(?:[-+=&;%@.w_]*)#?(?:[w]*))?)/", - "required": true - } - ], - "credential_fields": [] -} -``` - -## Events - -### `GET` `/:tenant_id/events` - -Retrieve a list of events using a cursor navigation. - -#### Supported parameters - -- `destination_id` `string | string[]` Return events for a specific destination -- `status` `success | failed` Return events with a specific failed or success status - -#### Response - -```json -[ - { - "id": "123", - "destination_id": "456", - "topic": "something.created", - "time": "2024-01-01T00:00:00Z", - "successful_at": "2024-01-01T00:00:00Z", - "metadata": { - "key": "value" // String, number or boolean - }, - "data": { - // Freeform JSON data - "hello": "world" - } - } -] -``` - -### `GET` `/:tenant_id/events/:event_id/deliveries` - -Retrieve a list of the delivery **attempts** with the associated responses. - -#### Response - -```json -[ - { - "delivered_at": "2024-01-01T00:00:00Z", - "status": "success", - "code": "200", // "OK", "ERR" or a valid HTTP status code - "response_data": { - "hello": "world" - } - } -] -``` - -### `GET` `/:tenant_id/destination/:destination_id/events` - -Retrieve a list of events using a cursor navigation. - -#### Supported parameters - -- `status` `success | failed` Return events with a specific failed or success status - -#### Response - -```json -[{ - "id": "123", - "destination_id": "456", - "topic": "something.created", - "time": "2024-01-01T00:00:00Z", - "successful_at": "2024-01-01T00:00:00Z" - "metadata": { - "key": "value" // String, number or boolean - }, - "data": { // Freeform JSON data - "hello": "world" - } -}] -``` - -### `GET` `/:tenant_id/destination/:destination_id/events/:event_id` - -Retrieve a specific event and its associated data. - -#### Response - -```json -[{ - "id": "123", - "destination_id": "456", - "topic": "something.created", - "time": "2024-01-01T00:00:00Z", - "successful_at": "2024-01-01T00:00:00Z" - "metadata": { - "key": "value" // String, number or boolean - }, - "data": { // Freeform JSON data - "hello": "world" - } -}] -``` - -### `POST` `/:tenant_id/destination/:destination_id/events/:event_id/retry` - -Submit an event for retry - -#### Response - -```json -{ "success": true } -``` diff --git a/examples/azure/diagnostics-clean.sh b/examples/azure/diagnostics-clean.sh index d9edb0f3f..44ec99fa8 100755 --- a/examples/azure/diagnostics-clean.sh +++ b/examples/azure/diagnostics-clean.sh @@ -63,7 +63,7 @@ run_cleanup() { TENANT_ID="diagnostics-tenant-x" echo " (Fetching destinations for tenant: $TENANT_ID...)" - DESTINATION_IDS=$(curl -sf -X GET "$base_url/api/v1/$TENANT_ID/destinations" \ + DESTINATION_IDS=$(curl -sf -X GET "$base_url/api/v1/tenants/$TENANT_ID/destinations" \ -H "Authorization: Bearer $API_KEY" | jq -r '.[].id') if [ -z "$DESTINATION_IDS" ]; then @@ -71,7 +71,7 @@ run_cleanup() { else for DEST_ID in $DESTINATION_IDS; do echo " (Deleting destination: $DEST_ID...)" - if ! curl -sf -X DELETE "$base_url/api/v1/$TENANT_ID/destinations/$DEST_ID" -H "Authorization: Bearer $API_KEY" >/dev/null; then + if ! curl -sf -X DELETE "$base_url/api/v1/tenants/$TENANT_ID/destinations/$DEST_ID" -H "Authorization: Bearer $API_KEY" >/dev/null; then echo " -> ❌ Failed to delete destination $DEST_ID." else echo " -> ✅ Destination $DEST_ID deleted." @@ -80,7 +80,7 @@ run_cleanup() { fi echo " (Deleting tenant: $TENANT_ID...)" - if ! curl -sf -X DELETE "$base_url/api/v1/$TENANT_ID" -H "Authorization: Bearer $API_KEY" >/dev/null; then + if ! curl -sf -X DELETE "$base_url/api/v1/tenants/$TENANT_ID" -H "Authorization: Bearer $API_KEY" >/dev/null; then echo " -> ❌ Failed to delete tenant $TENANT_ID." else echo " -> ✅ Tenant $TENANT_ID deleted." diff --git a/examples/azure/diagnostics.sh b/examples/azure/diagnostics.sh index 4a3fa2f1b..64394fb7f 100755 --- a/examples/azure/diagnostics.sh +++ b/examples/azure/diagnostics.sh @@ -182,7 +182,7 @@ run_api_tests() { fi echo " (Creating tenant: $TENANT_ID...)" - if ! curl -sf -X PUT "$base_url/api/v1/$TENANT_ID" -H "Authorization: Bearer $API_KEY" >/dev/null; then + if ! curl -sf -X PUT "$base_url/api/v1/tenants/$TENANT_ID" -H "Authorization: Bearer $API_KEY" >/dev/null; then echo " -> ❌ Failed to create tenant." if [[ "$base_url" == *"azurecontainerapps.io"* ]]; then echo " Fetching logs for '$AZURE_CONTAINER_APP_NAME'..." @@ -195,7 +195,7 @@ run_api_tests() { echo " -> ✅ Tenant created." echo " (Checking configured topics...)" - topics_response=$(curl -s -w "\n%{http_code}" -X GET "$base_url/api/v1/$TENANT_ID/topics" \ + topics_response=$(curl -s -w "\n%{http_code}" -X GET "$base_url/api/v1/tenants/$TENANT_ID/topics" \ -H "Authorization: Bearer $API_KEY") topics_http_code=$(echo "$topics_response" | tail -n1) @@ -212,7 +212,7 @@ run_api_tests() { fi echo " (Creating webhook destination...)" - DESTINATION_ID=$(curl -sf -X POST "$base_url/api/v1/$TENANT_ID/destinations" \ + DESTINATION_ID=$(curl -sf -X POST "$base_url/api/v1/tenants/$TENANT_ID/destinations" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $API_KEY" \ -d "{\"type\":\"webhook\",\"topics\":[\"*\"],\"config\":{\"url\":\"$WEBHOOK_URL\"}}" | jq -r .id) @@ -255,7 +255,7 @@ run_api_tests() { echo " -> ✅ Event published." echo " (Getting Outpost portal URL...)" - PORTAL_URL=$(curl -sf "$base_url/api/v1/$TENANT_ID/portal" -H "Authorization: Bearer $API_KEY" | jq -r .redirect_url) + PORTAL_URL=$(curl -sf "$base_url/api/v1/tenants/$TENANT_ID/portal" -H "Authorization: Bearer $API_KEY" | jq -r .redirect_url) if [ -z "$PORTAL_URL" ]; then echo " -> ⚠️ Could not retrieve portal URL." else @@ -263,7 +263,7 @@ run_api_tests() { fi echo " (Testing destination deletion...)" - if ! curl -sf -X DELETE "$base_url/api/v1/$TENANT_ID/destinations/$DESTINATION_ID" \ + if ! curl -sf -X DELETE "$base_url/api/v1/tenants/$TENANT_ID/destinations/$DESTINATION_ID" \ -H "Authorization: Bearer $API_KEY" >/dev/null; then echo " -> ❌ Failed to delete webhook destination." if [[ "$base_url" == *"azurecontainerapps.io"* ]]; then diff --git a/examples/demos/overview/README.md b/examples/demos/overview/README.md index 233a0bd16..e8741fa68 100644 --- a/examples/demos/overview/README.md +++ b/examples/demos/overview/README.md @@ -11,7 +11,7 @@ URL=your_webhook_url You'd do this whenever a new organization signups up. ```sh -curl --location --request PUT "localhost:3333/api/v1/$TENANT_ID" \ +curl --location --request PUT "localhost:3333/api/v1/tenants/$TENANT_ID" \ --header "Authorization: Bearer $API_KEY" ``` @@ -20,7 +20,7 @@ curl --location --request PUT "localhost:3333/api/v1/$TENANT_ID" \ When someone within an org wants to subscribe to an event, create a Destination: ```sh -curl --location "localhost:3333/api/v1/$TENANT_ID/destinations" \ +curl --location "localhost:3333/api/v1/tenants/$TENANT_ID/destinations" \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ @@ -77,7 +77,7 @@ Outpost comes with a pre-built portal that supports event destination management Event Inspection will be available next week (mid-March). Metric will be part of the BETA release. ```sh -curl "localhost:3333/api/v1/$TENANT_ID/portal" \ +curl "localhost:3333/api/v1/tenants/$TENANT_ID/portal" \ --header "Authorization: Bearer $API_KEY" ``` diff --git a/loadtest/docs/infra-aws.md b/loadtest/docs/infra-aws.md index b2f163abb..9c3959613 100644 --- a/loadtest/docs/infra-aws.md +++ b/loadtest/docs/infra-aws.md @@ -132,7 +132,7 @@ A successful health check will return a positive response indicating that Outpos Once Outpost is running, you can create a new tenant with: ```sh -curl -v --location --request PUT "$API_URL/api/v1/123" \ +curl -v --location --request PUT "$API_URL/api/v1/tenants/123" \ --header "Authorization: Bearer $API_KEY" ``` diff --git a/loadtest/infra/local/kubernetes/scripts/up.sh b/loadtest/infra/local/kubernetes/scripts/up.sh index 56f2c4348..30bcc0fbd 100755 --- a/loadtest/infra/local/kubernetes/scripts/up.sh +++ b/loadtest/infra/local/kubernetes/scripts/up.sh @@ -231,12 +231,12 @@ kubectl port-forward svc/outpost 3333:3333 -n $NAMESPACE curl http://localhost:3333/api/v1/healthz # Create tenant -curl -v -X PUT http://localhost:3333/api/v1/123 \ +curl -v -X PUT http://localhost:3333/api/v1/tenants/123 \ -H 'Authorization: Bearer $API_KEY' \ -H 'Content-Type: application/json' # Create a destination (replace URL with your endpoint) -WEBHOOK_URL='http://example.com/webhook' curl -v -X POST http://localhost:3333/api/v1/123/destinations \ +WEBHOOK_URL='http://example.com/webhook' curl -v -X POST http://localhost:3333/api/v1/tenants/123/destinations \ -H 'Authorization: Bearer $API_KEY' \ -H 'Content-Type: application/json' \ -d '{ @@ -249,11 +249,12 @@ WEBHOOK_URL='http://example.com/webhook' curl -v -X POST http://localhost:3333/a }' # Send an event -curl -v -X POST http://localhost:3333/api/v1/events \ +curl -v -X POST http://localhost:3333/api/v1/publish \ -H 'Authorization: Bearer $API_KEY' \ -H 'Content-Type: application/json' \ -d '{ - \"event\": \"test.event\", + \"tenant_id\": \"123\", + \"topic\": \"test.event\", \"data\": {\"hello\": \"world\"} }' diff --git a/loadtest/src/tests/events-throughput.ts b/loadtest/src/tests/events-throughput.ts index acb5ba164..b1dab9fe5 100644 --- a/loadtest/src/tests/events-throughput.ts +++ b/loadtest/src/tests/events-throughput.ts @@ -90,7 +90,7 @@ export function setup() { // Create tenant const tenantResponse = http.put( - `${config.env.api.baseUrl}/api/v1/${tenantId}`, + `${config.env.api.baseUrl}/api/v1/tenants/${tenantId}`, JSON.stringify({}), { headers } ); @@ -107,7 +107,7 @@ export function setup() { // Create destination const destinationResponse = http.post( - `${config.env.api.baseUrl}/api/v1/${tenantId}/destinations`, + `${config.env.api.baseUrl}/api/v1/tenants/${tenantId}/destinations`, JSON.stringify({ type: "webhook", topics: ["user.created"], diff --git a/loadtest/src/tests/events.ts b/loadtest/src/tests/events.ts index a2d734c01..475d4c2de 100644 --- a/loadtest/src/tests/events.ts +++ b/loadtest/src/tests/events.ts @@ -54,7 +54,7 @@ export default function (): void { if (__ITER === 0) { // Create tenant const tenantResponse = http.put( - `${config.env.api.baseUrl}/api/v1/${tenantId}`, + `${config.env.api.baseUrl}/api/v1/tenants/${tenantId}`, JSON.stringify({}), { headers } ); @@ -71,7 +71,7 @@ export default function (): void { // Create destination const destinationResponse = http.post( - `${config.env.api.baseUrl}/api/v1/${tenantId}/destinations`, + `${config.env.api.baseUrl}/api/v1/tenants/${tenantId}/destinations`, JSON.stringify({ type: "webhook", topics: ["user.created"], diff --git a/spec-sdk-tests/TEST_STATUS.md b/spec-sdk-tests/TEST_STATUS.md index 1723aa143..49d69d57a 100644 --- a/spec-sdk-tests/TEST_STATUS.md +++ b/spec-sdk-tests/TEST_STATUS.md @@ -150,17 +150,17 @@ All 7 Hookdeck tests have been marked with `.skip()`: 1. **Lines 61-64**: `should create a Hookdeck destination with valid config` (it.skip) 2. **Lines 66-79**: `should create a Hookdeck destination with array of topics` (it.skip) 3. **Lines 81-92**: `should create destination with user-provided ID` (it.skip) -4. **Lines 167-206**: `GET /api/v1/{tenant_id}/destinations/{id}` describe block (describe.skip) +4. **Lines 167-206**: `GET /api/v1/tenants/{tenant_id}/destinations/{id}` describe block (describe.skip) - `should retrieve an existing Hookdeck destination` - `should return 404 for non-existent destination` -5. **Lines 210-232**: `GET /api/v1/{tenant_id}/destinations` describe block (describe.skip) +5. **Lines 210-232**: `GET /api/v1/tenants/{tenant_id}/destinations` describe block (describe.skip) - `should list all destinations` - `should filter destinations by type` -6. **Lines 236-300**: `PATCH /api/v1/{tenant_id}/destinations/{id}` describe block (describe.skip) +6. **Lines 236-300**: `PATCH /api/v1/tenants/{tenant_id}/destinations/{id}` describe block (describe.skip) - `should update destination topics` - `should update destination credentials` - `should return 404 for updating non-existent destination` -7. **Lines 304-325**: `DELETE /api/v1/{tenant_id}/destinations/{id}` describe block (describe.skip) +7. **Lines 304-325**: `DELETE /api/v1/tenants/{tenant_id}/destinations/{id}` describe block (describe.skip) - `should delete an existing destination` - `should return 404 for deleting non-existent destination` diff --git a/spec-sdk-tests/tests/destinations/aws-kinesis.test.ts b/spec-sdk-tests/tests/destinations/aws-kinesis.test.ts index 7a259db37..1abd37a92 100644 --- a/spec-sdk-tests/tests/destinations/aws-kinesis.test.ts +++ b/spec-sdk-tests/tests/destinations/aws-kinesis.test.ts @@ -54,7 +54,7 @@ describe('AWS Kinesis Destinations - Contract Tests (SDK-based validation)', () } }); - describe('POST /api/v1/{tenant_id}/destinations - Create AWS Kinesis Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create AWS Kinesis Destination', () => { it('should create an AWS Kinesis destination with valid config', async () => { const destinationData = createAwsKinesisDestination(); const destination = await client.createDestination(destinationData); @@ -226,7 +226,7 @@ describe('AWS Kinesis Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve AWS Kinesis Destination', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve AWS Kinesis Destination', () => { let destinationId: string; before(async () => { @@ -271,7 +271,7 @@ describe('AWS Kinesis Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('GET /api/v1/{tenant_id}/destinations - List AWS Kinesis Destinations', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations - List AWS Kinesis Destinations', () => { before(async () => { // Create multiple AWS Kinesis destinations for listing await client.createDestination(createAwsKinesisDestination()); @@ -301,7 +301,7 @@ describe('AWS Kinesis Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('PATCH /api/v1/{tenant_id}/destinations/{id} - Update AWS Kinesis Destination', () => { + describe('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update AWS Kinesis Destination', () => { let destinationId: string; before(async () => { @@ -378,7 +378,7 @@ describe('AWS Kinesis Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete AWS Kinesis Destination', () => { + describe('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete AWS Kinesis Destination', () => { it('should delete an existing destination', async () => { const destinationData = createAwsKinesisDestination(); const destination = await client.createDestination(destinationData); diff --git a/spec-sdk-tests/tests/destinations/aws-s3.test.ts b/spec-sdk-tests/tests/destinations/aws-s3.test.ts index 56c7d614d..ffb7e348b 100644 --- a/spec-sdk-tests/tests/destinations/aws-s3.test.ts +++ b/spec-sdk-tests/tests/destinations/aws-s3.test.ts @@ -54,7 +54,7 @@ describe('AWS S3 Destinations - Contract Tests (SDK-based validation)', () => { } }); - describe('POST /api/v1/{tenant_id}/destinations - Create AWS S3 Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create AWS S3 Destination', () => { it('should create an AWS S3 destination with valid config', async () => { const destinationData = createAwsS3Destination(); const destination = await client.createDestination(destinationData); @@ -226,7 +226,7 @@ describe('AWS S3 Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve AWS S3 Destination', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve AWS S3 Destination', () => { let destinationId: string; before(async () => { @@ -271,7 +271,7 @@ describe('AWS S3 Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('GET /api/v1/{tenant_id}/destinations - List AWS S3 Destinations', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations - List AWS S3 Destinations', () => { before(async () => { // Create multiple AWS S3 destinations for listing await client.createDestination(createAwsS3Destination()); @@ -301,7 +301,7 @@ describe('AWS S3 Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('PATCH /api/v1/{tenant_id}/destinations/{id} - Update AWS S3 Destination', () => { + describe('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update AWS S3 Destination', () => { let destinationId: string; before(async () => { @@ -375,7 +375,7 @@ describe('AWS S3 Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete AWS S3 Destination', () => { + describe('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete AWS S3 Destination', () => { it('should delete an existing destination', async () => { const destinationData = createAwsS3Destination(); const destination = await client.createDestination(destinationData); diff --git a/spec-sdk-tests/tests/destinations/aws-sqs.test.ts b/spec-sdk-tests/tests/destinations/aws-sqs.test.ts index a6361df91..bedb2a259 100644 --- a/spec-sdk-tests/tests/destinations/aws-sqs.test.ts +++ b/spec-sdk-tests/tests/destinations/aws-sqs.test.ts @@ -54,7 +54,7 @@ describe('AWS SQS Destinations - Contract Tests (SDK-based validation)', () => { } }); - describe('POST /api/v1/{tenant_id}/destinations - Create AWS SQS Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create AWS SQS Destination', () => { it('should create an AWS SQS destination with valid config', async () => { const destinationData = createAwsSqsDestination(); const destination = await client.createDestination(destinationData); @@ -193,7 +193,7 @@ describe('AWS SQS Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve AWS SQS Destination', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve AWS SQS Destination', () => { let destinationId: string; before(async () => { @@ -237,7 +237,7 @@ describe('AWS SQS Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('GET /api/v1/{tenant_id}/destinations - List AWS SQS Destinations', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations - List AWS SQS Destinations', () => { before(async () => { // Create multiple AWS SQS destinations for listing await client.createDestination(createAwsSqsDestination()); @@ -266,7 +266,7 @@ describe('AWS SQS Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('PATCH /api/v1/{tenant_id}/destinations/{id} - Update AWS SQS Destination', () => { + describe('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update AWS SQS Destination', () => { let destinationId: string; before(async () => { @@ -342,7 +342,7 @@ describe('AWS SQS Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete AWS SQS Destination', () => { + describe('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete AWS SQS Destination', () => { it('should delete an existing destination', async () => { const destinationData = createAwsSqsDestination(); const destination = await client.createDestination(destinationData); diff --git a/spec-sdk-tests/tests/destinations/azure-servicebus.test.ts b/spec-sdk-tests/tests/destinations/azure-servicebus.test.ts index 9161d57f0..00e4f74b7 100644 --- a/spec-sdk-tests/tests/destinations/azure-servicebus.test.ts +++ b/spec-sdk-tests/tests/destinations/azure-servicebus.test.ts @@ -54,7 +54,7 @@ describe('Azure Service Bus Destinations - Contract Tests (SDK-based validation) } }); - describe('POST /api/v1/{tenant_id}/destinations - Create Azure Service Bus Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create Azure Service Bus Destination', () => { it('should create an Azure Service Bus destination with valid config', async () => { const destinationData = createAzureServiceBusDestination(); const destination = await client.createDestination(destinationData); @@ -193,7 +193,7 @@ describe('Azure Service Bus Destinations - Contract Tests (SDK-based validation) }); }); - describe('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve Azure Service Bus Destination', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve Azure Service Bus Destination', () => { let destinationId: string; before(async () => { @@ -237,7 +237,7 @@ describe('Azure Service Bus Destinations - Contract Tests (SDK-based validation) }); }); - describe('GET /api/v1/{tenant_id}/destinations - List Azure Service Bus Destinations', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations - List Azure Service Bus Destinations', () => { before(async () => { // Create multiple Azure Service Bus destinations for listing await client.createDestination(createAzureServiceBusDestination()); @@ -266,7 +266,7 @@ describe('Azure Service Bus Destinations - Contract Tests (SDK-based validation) }); }); - describe('PATCH /api/v1/{tenant_id}/destinations/{id} - Update Azure Service Bus Destination', () => { + describe('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update Azure Service Bus Destination', () => { let destinationId: string; before(async () => { @@ -340,7 +340,7 @@ describe('Azure Service Bus Destinations - Contract Tests (SDK-based validation) }); }); - describe('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete Azure Service Bus Destination', () => { + describe('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete Azure Service Bus Destination', () => { it('should delete an existing destination', async () => { const destinationData = createAzureServiceBusDestination(); const destination = await client.createDestination(destinationData); diff --git a/spec-sdk-tests/tests/destinations/gcp-pubsub.test.ts b/spec-sdk-tests/tests/destinations/gcp-pubsub.test.ts index 1e507b372..c0ac1bf49 100644 --- a/spec-sdk-tests/tests/destinations/gcp-pubsub.test.ts +++ b/spec-sdk-tests/tests/destinations/gcp-pubsub.test.ts @@ -55,7 +55,7 @@ describe('GCP Pub/Sub Destinations - Contract Tests (SDK-based validation)', () } }); - describe('POST /api/v1/{tenant_id}/destinations - Create GCP Pub/Sub Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create GCP Pub/Sub Destination', () => { it('should create a GCP Pub/Sub destination with valid config', async () => { const destination = await client.createDestination({ type: 'gcp_pubsub', @@ -301,7 +301,7 @@ describe('GCP Pub/Sub Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve GCP Pub/Sub Destination', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve GCP Pub/Sub Destination', () => { let destinationId: string; before(async () => { @@ -376,7 +376,7 @@ describe('GCP Pub/Sub Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('GET /api/v1/{tenant_id}/destinations - List GCP Pub/Sub Destinations', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations - List GCP Pub/Sub Destinations', () => { before(async () => { // Create multiple GCP Pub/Sub destinations for listing await client.createDestination({ @@ -429,7 +429,7 @@ describe('GCP Pub/Sub Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('PATCH /api/v1/{tenant_id}/destinations/{id} - Update GCP Pub/Sub Destination', () => { + describe('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update GCP Pub/Sub Destination', () => { let destinationId: string; before(async () => { @@ -537,7 +537,7 @@ describe('GCP Pub/Sub Destinations - Contract Tests (SDK-based validation)', () }); }); - describe('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete GCP Pub/Sub Destination', () => { + describe('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete GCP Pub/Sub Destination', () => { it('should delete an existing destination', async () => { // Create a destination to delete const destination = await client.createDestination({ diff --git a/spec-sdk-tests/tests/destinations/hookdeck.test.ts b/spec-sdk-tests/tests/destinations/hookdeck.test.ts index 1d897ec20..38bc6b2c5 100644 --- a/spec-sdk-tests/tests/destinations/hookdeck.test.ts +++ b/spec-sdk-tests/tests/destinations/hookdeck.test.ts @@ -54,7 +54,7 @@ describe('Hookdeck Destinations - Contract Tests (SDK-based validation)', () => } }); - describe('POST /api/v1/{tenant_id}/destinations - Create Hookdeck Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create Hookdeck Destination', () => { // TODO: Re-enable these tests once backend supports test mode without external API verification // Issue: Backend calls external Hookdeck API to verify tokens during destination creation // See: internal/destregistry/providers/desthookdeck/desthookdeck.go:243 @@ -164,7 +164,7 @@ describe('Hookdeck Destinations - Contract Tests (SDK-based validation)', () => // TODO: Re-enable these tests once backend supports test mode without external API verification // Issue: Backend calls external Hookdeck API to verify tokens during destination creation - describe.skip('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve Hookdeck Destination', () => { + describe.skip('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve Hookdeck Destination', () => { let destinationId: string; before(async () => { @@ -209,7 +209,7 @@ describe('Hookdeck Destinations - Contract Tests (SDK-based validation)', () => // TODO: Re-enable these tests once backend supports test mode without external API verification // Issue: Backend calls external Hookdeck API to verify tokens during destination creation - describe.skip('GET /api/v1/{tenant_id}/destinations - List Hookdeck Destinations', () => { + describe.skip('GET /api/v1/tenants/{tenant_id}/destinations - List Hookdeck Destinations', () => { before(async () => { // Create multiple Hookdeck destinations for listing await client.createDestination(createHookdeckDestination()); @@ -237,7 +237,7 @@ describe('Hookdeck Destinations - Contract Tests (SDK-based validation)', () => // TODO: Re-enable these tests once backend supports test mode without external API verification // Issue: Backend calls external Hookdeck API to verify tokens during destination creation - describe.skip('PATCH /api/v1/{tenant_id}/destinations/{id} - Update Hookdeck Destination', () => { + describe.skip('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update Hookdeck Destination', () => { let destinationId: string; before(async () => { @@ -298,7 +298,7 @@ describe('Hookdeck Destinations - Contract Tests (SDK-based validation)', () => // TODO: Re-enable these tests once backend supports test mode without external API verification // Issue: Backend calls external Hookdeck API to verify tokens during destination creation - describe.skip('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete Hookdeck Destination', () => { + describe.skip('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete Hookdeck Destination', () => { it('should delete an existing destination', async () => { const destinationData = createHookdeckDestination(); const destination = await client.createDestination(destinationData); diff --git a/spec-sdk-tests/tests/destinations/rabbitmq.test.ts b/spec-sdk-tests/tests/destinations/rabbitmq.test.ts index b081066de..edfca63ce 100644 --- a/spec-sdk-tests/tests/destinations/rabbitmq.test.ts +++ b/spec-sdk-tests/tests/destinations/rabbitmq.test.ts @@ -54,7 +54,7 @@ describe('RabbitMQ Destinations - Contract Tests (SDK-based validation)', () => } }); - describe('POST /api/v1/{tenant_id}/destinations - Create RabbitMQ Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create RabbitMQ Destination', () => { it('should create a RabbitMQ destination with valid config', async () => { const destinationData = createRabbitMqDestination(); const destination = await client.createDestination(destinationData); @@ -226,7 +226,7 @@ describe('RabbitMQ Destinations - Contract Tests (SDK-based validation)', () => }); }); - describe('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve RabbitMQ Destination', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve RabbitMQ Destination', () => { let destinationId: string; before(async () => { @@ -271,7 +271,7 @@ describe('RabbitMQ Destinations - Contract Tests (SDK-based validation)', () => }); }); - describe('GET /api/v1/{tenant_id}/destinations - List RabbitMQ Destinations', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations - List RabbitMQ Destinations', () => { before(async () => { // Create multiple RabbitMQ destinations for listing await client.createDestination(createRabbitMqDestination()); @@ -301,7 +301,7 @@ describe('RabbitMQ Destinations - Contract Tests (SDK-based validation)', () => }); }); - describe('PATCH /api/v1/{tenant_id}/destinations/{id} - Update RabbitMQ Destination', () => { + describe('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update RabbitMQ Destination', () => { let destinationId: string; before(async () => { @@ -375,7 +375,7 @@ describe('RabbitMQ Destinations - Contract Tests (SDK-based validation)', () => }); }); - describe('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete RabbitMQ Destination', () => { + describe('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete RabbitMQ Destination', () => { it('should delete an existing destination', async () => { const destinationData = createRabbitMqDestination(); const destination = await client.createDestination(destinationData); diff --git a/spec-sdk-tests/tests/destinations/webhook.test.ts b/spec-sdk-tests/tests/destinations/webhook.test.ts index a80b8daf7..1dabf06e4 100644 --- a/spec-sdk-tests/tests/destinations/webhook.test.ts +++ b/spec-sdk-tests/tests/destinations/webhook.test.ts @@ -54,7 +54,7 @@ describe('Webhook Destinations - Contract Tests (SDK-based validation)', () => { } }); - describe('POST /api/v1/{tenant_id}/destinations - Create Webhook Destination', () => { + describe('POST /api/v1/tenants/{tenant_id}/destinations - Create Webhook Destination', () => { it('should create a webhook destination with valid config', async () => { const destinationData = createWebhookDestination(); const destination = await client.createDestination(destinationData); @@ -160,7 +160,7 @@ describe('Webhook Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('GET /api/v1/{tenant_id}/destinations/{id} - Retrieve Webhook Destination', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{id} - Retrieve Webhook Destination', () => { let destinationId: string; before(async () => { @@ -204,7 +204,7 @@ describe('Webhook Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('GET /api/v1/{tenant_id}/destinations - List Webhook Destinations', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations - List Webhook Destinations', () => { before(async () => { // Create multiple webhook destinations for listing await client.createDestination(createWebhookDestination()); @@ -233,7 +233,7 @@ describe('Webhook Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('PATCH /api/v1/{tenant_id}/destinations/{id} - Update Webhook Destination', () => { + describe('PATCH /api/v1/tenants/{tenant_id}/destinations/{id} - Update Webhook Destination', () => { let destinationId: string; before(async () => { @@ -296,7 +296,7 @@ describe('Webhook Destinations - Contract Tests (SDK-based validation)', () => { }); }); - describe('DELETE /api/v1/{tenant_id}/destinations/{id} - Delete Webhook Destination', () => { + describe('DELETE /api/v1/tenants/{tenant_id}/destinations/{id} - Delete Webhook Destination', () => { it('should delete an existing destination', async () => { const destinationData = createWebhookDestination(); const destination = await client.createDestination(destinationData); diff --git a/spec-sdk-tests/tests/events.test.ts b/spec-sdk-tests/tests/events.test.ts index 14684e3a5..abebc3886 100644 --- a/spec-sdk-tests/tests/events.test.ts +++ b/spec-sdk-tests/tests/events.test.ts @@ -132,7 +132,7 @@ describe('Events - Status Field Tests (PR #491)', () => { } }); - describe('GET /api/v1/{tenant_id}/destinations/{destination_id}/events - Event Status Field', () => { + describe('GET /api/v1/tenants/{tenant_id}/destinations/{destination_id}/events - Event Status Field', () => { it('should include status field in events returned from listByDestination', async function () { // Increase timeout for this test as it involves publishing and waiting for event delivery this.timeout(45000); @@ -225,7 +225,7 @@ describe('Events - Status Field Tests (PR #491)', () => { }); }); - describe('GET /api/v1/{tenant_id}/events - Tenant Events Status Field', () => { + describe('GET /api/v1/tenants/{tenant_id}/events - Tenant Events Status Field', () => { it('should include status field in events returned from tenant events list', async function () { // Increase timeout for this test as it involves publishing and waiting for event delivery this.timeout(45000);