# RudderStack Test API


This guide details the various endpoints of the Test API.

## Overview

The **RudderStack Test API** offers two endpoints to verify successful event transformation and delivery for a given source-destination setup, without having to refer to the [Live Events]({{< ref "monitor/live-events.md" >}}) tab.

{{< warning >}}
The Test API is currently not supported for [RudderStack Open Source]({{< ref "get-started/rudderstack-open-source/" >}}).
 
Also, some destinations like Apache Kafka, Google Pub/Sub, Google Sheets, etc. are not supported by this API. For the complete list, see the [FAQ](#faq) section below.
{{< /warning >}}

## Prerequisites

- Set up a source-destination connection in RudderStack. See the [Quickstart]({{< ref "data-pipelines/event-stream/quickstart/" >}}) guide for detailed steps.
- Generate a [workspace-level Service Access Token]({{< ref "access-management/service-access-tokens.md#workspace-sat" >}}) in your RudderStack workspace.

{{< info >}}
The workspace-level Service Access Token has **Read** permissions by default. You do not need to assign any [resource permissions]({{< ref "access-management/policies-overview.md#resource-permissions" >}}) while generating the token.
{{< /info >}}

#### Token permissions for legacy RBAC system

If you are on the [legacy Permissions Management (RBAC) system]({{< ref "archive/dashboard-guides/user-management.md" >}}), your workspace-level Service Access Token should have minimum **Viewer** permissions.

See [this documentation]({{< ref "archive/dashboard-guides/service-access-tokens.md#generate-service-access-token" >}}) for more information on generating the token.

{{< image src="images/access-management/permissions/legacy/viewer.webp" alt="workspace-level Service Access Token with Viewer permission" >}}

## API authorization

The Test API uses **Basic Authentication** for authenticating all requests.

If you're using Postman, authenticate the API by including an empty string (`""`) as the username and your [workspace-level Service Access Token]({{< ref "access-management/service-access-tokens.md#generate-service-access-token" >}}) as the password in the **Authorization** tab.

{{< info >}}
RudderStack recommends using:

- [Service Access Tokens (SATs)]({{< ref "access-management/service-access-tokens.md#generate-service-access-token" >}}) for production use cases that require shared access to the services and resources across the organization or workspace. 
- [Personal Access Tokens (PATs)]({{< ref "access-management/personal-access-tokens.md" >}}) for testing a service/feature or personal use cases.
{{< /info >}}

You can also pass your Service Access Token in the authorization header directly:

```bash
Authorization: Basic {Base64Encoded(:<SERVICE_ACCESS_TOKEN>)}
```

An example is shown below:

- Username: `""` (empty string)
- Service Access Token: `<SERVICE_ACCESS_TOKEN>`
- Header: `Basic {Base64Encoded(:<SERVICE_ACCESS_TOKEN>)}`

{{< tip >}}
Verify that the Service Access Token is valid if you get an **Invalid Authorization Header** error.
{{< /tip >}}

## Base URL

Use the base URL for your API requests depending on your region:

{{< tabs tabTotal="2" >}}
{{% tab tabName="Standard" %}}
```text
https://api.rudderstack.com
```
{{% /tab %}}
{{% tab tabName="EU-Standard" %}}
```text
https://api.eu.rudderstack.com
```
{{% /tab %}}
{{< /tabs >}}

## Verify destination events {#verifying-destination-events}

This request verifies if the test events are successfully transformed and delivered to the specified destination.

{{< info >}}
The `/testDestination` endpoint does not require a source to be connected to the destination.
{{< /info >}}

{{% api-method method="post" host="https://api.rudderstack.com" path="/v0/testDestination/{destinationId}" %}}

**Path parameters**

{{< query-params keyname="destinationId" valuename="Resource ID of the destination configured in the RudderStack dashboard." keytype="Required" datatype="String" >}}

{{< image src="images/api/test-api/destination-id.webp" alt="Destination ID" >}}

**Query parameters**

{{< query-params keyname="force" valuename="Set to `true` to send the event to a destination that is disabled. <br />See [Verify events for disabled destinations](#verify-events-for-disabled-destinations-1)." keytype="Optional" datatype="Boolean" >}}
<hr/>

**Request body**

{{< query-paramsList >}}
  {{< query-params keyname="message" valuename="RudderStack HTTP payload to verify. <br />See [Supported message types](#supported-message-types) for the accepted `type` values." keytype="Required" datatype="Object" >}}
  {{< query-params keyname="stage" valuename="Pipeline stages through which the API verifies the event payload.<br />See [Verification stages](#verification-stages) for details." keytype="Optional" datatype="Object" >}}
  {{< query-params keyname="user_transform" valuename="Set to `true` to check if a user transformation is connected to the destination and return the transformed event." keytype="Optional" datatype="Boolean" >}}
  {{< query-params keyname="dest_transform" valuename="Set to `true` to return the payload after it is transformed into a destination-specific format." keytype="Optional" datatype="Boolean" >}}
  {{< query-params keyname="send_to_destination" valuename="Set to `true` to send the event to the destination and return the destination's response." keytype="Optional" datatype="Boolean" >}}
{{< /query-paramsList >}}

**Example request**

{{< tabs tabTotal="2" >}}
{{% tab tabName="HTTP" %}}
```http
POST /v0/testDestination/1zl4i0J8M8T7sozoLnueW46RVYe HTTP/1.1
Host: api.rudderstack.com
Content-Type: application/json
Authorization: Basic {Base64Encoded(:<SERVICE_ACCESS_TOKEN>)}

{
  "message": {
    "context": {
      "traits": {
        "firstName": "James",
        "lastName": "Doe"
      }
    },
    "type": "identify",
    "userId": "abc@123.com"
  },
  "stage": {
    "user_transform": true,
    "dest_transform": true,
    "send_to_destination": true
  }
}
```
{{% /tab %}}
{{% tab tabName="CURL" %}}
```bash
curl --location --request POST 'https://api.rudderstack.com/v0/testDestination/1zl4i0J8M8T7sozoLnueW46RVYe' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {Base64Encoded(:<SERVICE_ACCESS_TOKEN>)}' \
--data-raw '{
  "message": {
    "context": {
      "traits": {
        "firstName": "James",
        "lastName": "Doe"
      }
    },
    "type": "identify",
    "userId": "abc@123.com"
  },
  "stage": {
    "user_transform": true,
    "dest_transform": true,
    "send_to_destination": true
  }
}'
```
{{% /tab %}}
{{< /tabs >}}

**Example response**

```json
{
  "destinationId": "1zl4i0J8M8T7sozoLnueW46RVYe",
  "destination": "WEBHOOK",
  "destinationName": "test-webhook-dest",
  "data": [{
    "user_transformed_payload": {
      "error": "Transformation VersionID not found"
    },
    "dest_transformed_payload": [{
      "version": "1",
      "type": "REST",
      "method": "POST",
      "endpoint": "https://webhook.site/9d5e3e43-6c2b-4b84-be9f-0147347b4cdf",
      "headers": {
        "content-type": "application/json"
      },
      "params": {},
      "body": {
        "JSON": {
          "context": {
            "traits": {
              "firstName": "James",
              "lastName": "Doe"
            }
          },
          "type": "identify",
          "userId": "abc@123.com"
        },
        "JSON_ARRAY": {},
        "XML": {},
        "FORM": {}
      },
      "files": {}
    }],
    "destination_response": [{
      "success": false,
      "error": {
        "message": "Token not found",
        "id": null
      }
    }],
    "destination_response_status": [
      404
    ]
  }]
}
```

### Verify events for disabled destinations {#verify-events-for-disabled-destinations-1}

If the `/testDestination` endpoint is used to verify the events sent to a disabled destination, the API will return the following response:

```json
{
  "destinationId": "<destination_ID>",
  "destinationName": "<destination_name>",
  "error": "Destination with id <destination_ID> is disabled"
}
```

A sample response highlighting the above error is shown below:

```json
{
  "destinationId": "1zl4i0J8M8T7sozoLnueW46RVYe",
  "destinationName": "test-webhook-dest",
  "error": "Destination with id 1zl4i0J8M8T7sozoLnueW46RVYe is disabled"
}
```

To override this behavior and send the event to a disabled destination, you can call the `/testDestination` endpoint with the query parameter `force=true`:

```bash
https://api.rudderstack.com/v0/testDestination/<destination_ID>?force=true
```

## Verify source events {#verifying-source-events}

This request verifies if the test events are successfully sent from the specified source and delivered to all connected destinations.

{{< info >}}
Note that:

- The `/testSource` endpoint requires the specified source to be connected to at least one destination.
- The `/testSource` endpoint essentially calls the `/testDestination` endpoint for each destination connected to that source and returns an array of responses.
{{< /info >}}

{{% api-method method="post" host="https://api.rudderstack.com" path="/v0/testSource/{sourceId}" %}}

**Path parameters**

{{< query-params keyname="sourceId" valuename="Resource ID of the source configured in the RudderStack dashboard." keytype="Required" datatype="String" >}}

{{< image src="images/api/test-api/source-id.webp" alt="Source ID" >}}

**Query parameters**

{{< query-params keyname="force" valuename="Set to `true` to send the event from a disabled source, or to a disabled destination. <br />See [Verify events from a disabled source](#verify-events-from-a-disabled-source) and [Verify events for disabled destinations](#verify-events-for-disabled-destinations) for more information." keytype="Optional" datatype="Boolean" >}}
<hr/>

**Request body**

{{< query-paramsList >}}
  {{< query-params keyname="message" valuename="RudderStack HTTP payload to verify. <br />See [Supported message types](#supported-message-types) for the accepted `type` values." keytype="Required" datatype="Object" >}}
  {{< query-params keyname="stage" valuename="Pipeline stages through which the API verifies the event payload.<br />See [Verification stages](#verification-stages) for details." keytype="Optional" datatype="Object" >}}
  {{< query-params keyname="user_transform" valuename="Set to `true` to check if a user transformation is connected to the destination and return the transformed event." keytype="Optional" datatype="Boolean" >}}
  {{< query-params keyname="dest_transform" valuename="Set to `true` to return the payload after it is transformed into a destination-specific format." keytype="Optional" datatype="Boolean" >}}
  {{< query-params keyname="send_to_destination" valuename="Set to `true` to send the event to the destination and return the destination's response." keytype="Optional" datatype="Boolean" >}}
{{< /query-paramsList >}}

**Example request**

{{< tabs tabTotal="2" >}}
{{% tab tabName="HTTP" %}}
```http
POST /v0/testSource/1zlmsBMe1dcPbu3u6NTZFUFBrNQ HTTP/1.1
Host: api.rudderstack.com
Content-Type: application/json
Authorization: Basic {Base64Encoded(:<SERVICE_ACCESS_TOKEN>)}

{
  "message": {
    "context": {
      "traits": {
        "firstName": "James",
        "lastName": "Doe"
      }
    },
    "type": "identify",
    "userId": "abc@123.com"
  },
  "stage": {
    "user_transform": true,
    "dest_transform": true,
    "send_to_destination": true
  }
}
```
{{% /tab %}}
{{% tab tabName="CURL" %}}
```bash
curl --location --request POST 'https://api.rudderstack.com/v0/testSource/1zlmsBMe1dcPbu3u6NTZFUFBrNQ' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {Base64Encoded(:<SERVICE_ACCESS_TOKEN>)}' \
--data-raw '{
  "message": {
    "context": {
      "traits": {
        "firstName": "James",
        "lastName": "Doe"
      }
    },
    "type": "identify",
    "userId": "abc@123.com"
  },
  "stage": {
    "user_transform": true,
    "dest_transform": true,
    "send_to_destination": true
  }
}'
```
{{% /tab %}}
{{< /tabs >}}

**Example response**

```json
[{
    "destinationId": "1tIgXcaRnQDlBBtxlJMGGHFUWGb",
    "destinationName": "salesforce",
    "error": "Destination with id 1tIgXcaRnQDlBBtxlJMGGHFUWGb is disabled"
  },
  {
    "destinationId": "1zl4i0J8M8T7sozoLnueW46RVYe",
    "destination": "WEBHOOK",
    "destinationName": "test-webhook-dest",
    "data": [{
      "user_transformed_payload": {
        "error": "Transformation VersionID not found"
      },
      "dest_transformed_payload": [{
        "version": "1",
        "type": "REST",
        "method": "POST",
        "endpoint": "https://webhook.site/9d5e3e43-6c2b-4b84-be9f-0147347b4cdf",
        "headers": {
          "content-type": "application/json"
        },
        "params": {},
        "body": {
          "JSON": {
            "context": {
              "traits": {
                "firstName": "James",
                "lastName": "Doe"
              }
            },
            "type": "identify",
            "userId": "abc@123.com"
          },
          "JSON_ARRAY": {},
          "XML": {},
          "FORM": {}
        },
        "files": {}
      }],
      "destination_response": [{
        "success": false,
        "error": {
          "message": "Token not found",
          "id": null
        }
      }],
      "destination_response_status": [
        404
      ]
    }]
  },
  {
    "destinationId": "1tKpO0kantcKjd5czXVD1cdwUBE",
    "destination": "MARKETO",
    "destinationName": "Marketo",
    "data": [{
      "user_transformed_payload": {
        "error": "Transformation VersionID not found"
      },
      "dest_transformed_payload": [{
        "version": "1",
        "type": "REST",
        "method": "POST",
        "endpoint": "https://585-AXP-425.mktorest.com/rest/v1/leads.json",
        "headers": {
          "Authorization": "Bearer f54f0384-583b-4718-b533-38aaed4bc5ae:cd",
          "Content-Type": "application/json"
        },
        "params": {},
        "body": {
          "JSON": {
            "action": "createOrUpdate",
            "input": [{
              "FirstName": "James",
              "LastName": "Doe",
              "lastName": "Doe",
              "firstName": "James",
              "id": 1328262,
              "userId": "abc@123.com"
            }],
            "lookupField": "id"
          },
          "JSON_ARRAY": {},
          "XML": {},
          "FORM": {}
        },
        "files": {}
      }],
      "destination_response": [{
        "requestId": "555d#17d31b0a392",
        "result": [{
          "id": 1328262,
          "status": "updated"
        }],
        "success": true
      }],
      "destination_response_status": [
        200
      ]
    }]
  }
]
```

The above response indicates that the source with the source ID `1zlmsBMe1dcPbu3u6NTZFUFBrNQ` is connected to three destinations: 

* Salesforce, a disabled destination
* Webhook, with no transformation specified in the dashboard, and
* Marketo

### Verify events from a disabled source {#verify-events-from-a-disabled-source}

If the `/testSource` endpoint is used to verify the events sent from a disabled source, the API will send the following response:

```json
{
  "message": "Source with <source_ID> is disabled"
}
```

To override this behavior and send the event from a disabled source to all connected destinations, you can call the `/testSource` endpoint with the query parameter `force=true`:

```bash
https://api.rudderstack.com/v0/testSource/<source_ID>?force=true
```

### Verify events for disabled destinations {#verify-events-for-disabled-destinations}

If the `/testSource` endpoint is used to verify the events sent to a disabled destination, the API will send the following response:

```json
{
  "destinationId": "<destination_ID>",
  "destinationName": "<destination_name>",
  "error": "Destination with id <destination_ID> is disabled"
}
```

To override this behavior and send the event to a disabled destination, you can call the `/testSource` endpoint with the query parameter `force=true`:

```bash
https://api.rudderstack.com/v0/testSource/<source_ID>?force=true
```

## Verification stages

The request body for the `testDestination` and `testSource` endpoints of this API is as shown:

```json
{
  "stage": {
    "user_transform": true,
    "dest_transform": true,
    "send_to_destination": true
  },
  "message": {
    // RudderStack HTTP Payload (identify, track, etc.)
  }
}
```

Here, `stage` essentially defines the different stages enabled in the pipeline through which the API verifies the event payload. These stages are:

* `user_transform`
* `dest_transform`
* `send_to_destination`

The following sections define each of these stages in detail.

### User transformation

If `user_transform` is set to `true`, the API checks if a user transformation is connected to a destination and returns the transformed event as a response. If set to `false`, the API skips this stage completely and moves to the next stage (`dest_transform`).

Note the following:

* Suppose you set `user_transform` to `true`, but no user transformation is specified while configuring the destination in the dashboard. In this case, the API returns the following response before skipping to the next stage:

```json
"user_transformed_payload": {
  "error": "Transformation VersionID not found"
}
```

* If an error occurs while applying the transformation to the payload, the API returns an error, and the next stages are aborted.

A sample API response is shown below:

```json
[{
  "data": [{
    "user_transformed_payload": {
      "error": "Error: Error."
    },
    "dest_transformed_payload": {
      "error": "error encountered in user_transformation stage. Aborting."
    },
    "destination_response": {
      "error": "error encountered in dest_transformation stage. Aborting."
    }
  }]
}]
```

### Destination transformation

If `dest_transform` is set to `true`, the API returns the transformer response. This response shows the payload after it has been transformed into a destination-specific format.

A sample API response when `dest_transform` is set to `true` is shown below:

```json
"dest_transformed_payload": [{
  "version": "1",
  "type": "REST",
  "method": "POST",
  "endpoint": "https://webhook.site/9d5e3e43-6c2b-4b84-be9f-0147347b4cdf",
  "headers": {
    "content-type": "application/json"
  },
  "params": {},
  "body": {
    "JSON": {
      "context": {
        "traits": {
          "firstName": "James",
          "lastName": "Doe"
        }
      },
      "type": "identify",
      "userId": "abc@123.com"
    },
    "XML": {},
    "FORM": {}
  },
  "files": {}
}]
```

### Send to destination

When `send_to_destination` is set to `true`, the event is sent to the destination and the API returns the response. If set to `false`, this stage is skipped completely.

A sample API response when `send_to_destination` is set to `true` is shown below:

```json
"destination_response": [{
  "status": "success",
  "processed": 1,
  "unprocessed": []
}],
"destination_response_status": [200]
```

## Supported message types

The Test API supports the following message `type`:

* `identify`
* `track`
* `page`
* `screen`
* `group`
* `alias`

For more information on these message types, see the [RudderStack Events Specification]({{< ref "event-spec/standard-events/_index.md" >}}) guide.

If you specify any other message `type` apart from the supported events mentioned in the above list, you will get the following error:

```json
{
    "message": "message type is unsupported"
}
```

You will get the following error message if no message `type` is specified in the payload, or if `type` is not a string:

```json
{
    "message": "message type missing or invalid"
}
```

## FAQ

#### Which destinations are not supported by the Test API?

The Test API does not support the destinations that leverage the [rudder-server](https://github.com/rudderlabs/rudder-server) to send the test events. These destinations include:

* [Amazon Kinesis]({{< ref "destinations/streaming-destinations/amazon-kinesis.md" >}})
* [Apache Kafka]({{< ref "destinations/streaming-destinations/kafka.md" >}})
* [Azure Event Hub]({{< ref "destinations/streaming-destinations/azure-event-hubs.md" >}})
* [Amazon Kinesis Firehose]({{< ref "destinations/streaming-destinations/amazon-kinesis-firehose.md" >}})
* [Amazon EventBridge]({{< ref "destinations/streaming-destinations/amazon-eventbridge.md" >}})
* [Amazon Personalize]({{< ref "destinations/streaming-destinations/aws-personalize.md" >}})
* [Confluent Cloud]({{< ref "destinations/streaming-destinations/confluent-cloud.md" >}})
* [Google Pub/Sub]({{< ref "destinations/streaming-destinations/google-pub-sub.md" >}})
* [Google Sheets]({{< ref "destinations/streaming-destinations/google-sheets.md" >}})
* [Redis]({{< ref "destinations/streaming-destinations/redis.md" >}})
* [BigQuery Stream]({{< ref "destinations/streaming-destinations/bigquery-stream.md" >}})
* [Amazon S3]({{< ref "destinations/streaming-destinations/amazon-s3.md" >}})
* [Azure Blob Storage]({{< ref "destinations/streaming-destinations/microsoft-azure-blob-storage.md" >}})
* [DigitalOcean Spaces]({{< ref "destinations/streaming-destinations/digitalocean-spaces.md" >}})
* [Google Cloud Storage]({{< ref "destinations/streaming-destinations/google-cloud-storage.md" >}})
* [MinIO]({{< ref "destinations/streaming-destinations/minio.md" >}})

{{< warning >}}
The Test API also does not support the [data warehouse destinations]({{< ref "destinations/warehouse-destinations/_index.md" >}}).
{{< /warning >}}

#### What happens if `type` is not included in the event payload?

`type` refers to the event type in the payload:

* If `type` is missing in the event payload, the API returns the following error:

```json
{
    "message": "message type missing or invalid"
}
```

* If you set `type` to any value other than `identify`, `track`, `page`, `screen`, `group`, or `alias`, the API returns the following error:

```json
{
    "message": "message type is unsupported"
}
```

See the [Supported message types](#supported-message-types) section for more information.

#### Can I disable a particular verification stage in the `stage` object?

Yes. Set the corresponding parameter to `false` and the API skips that stage. For example, the following `stage` object skips the user transformation check:

```json
{
  "stage": {
    "user_transform": false,
    "dest_transform": true,
    "send_to_destination": true
  }
}
```

See the [Verification stages](#verification-stages) section for more information on each of the `stage` object parameters.

<br />
