---
title: "Setting up API Hooks"
canonical: "https://red-ant-documentation.refined.site/space/RDD/1057030148/Setting%20up%20API%20Hooks"
format: markdown
---
## Introduction

RetailOS supports integrating custom logic into its API workflows through synchronous API hooks. These hooks allow developers to connect RetailOS endpoints with external services, enhancing or replacing the default platform functionality. API hooks can be configured using the `apiHooks` resource to achieve seamless integration with third-party systems.

## Quickstart

### Required Configuration

To set up an API hook, the following configuration parameters are necessary:

- **Endpoint Method**: The HTTP method of the endpoint (e.g., `GET`).
- **Endpoint Path**: The path of the RetailOS API endpoint (e.g., `/customers/:id`).
- **Validation Schema Name**: The schema used to validate the response (e.g., `getCustomerResponseSchema`).
- **Hook Type**: The type of hook (`replace`, `after`, or `forward`).
- **Name**: A descriptive name for the API hook (e.g., `RA Salesforce OCAPI integration`).
- **Config Method**: The HTTP method for the external service (e.g., `POST`).
- **Config URL**: The URL of the external service (e.g., `<http://localhost:8080/v2/salesforce/customer/getOne`).>

### Example cURL Request

Here's an example cURL request to set up a "replace" API hook:

```shell
curl \
-X POST "<http://localhost:3000/v2/api-hooks>" \
-H "accept: application/json" \
-H "Authorization: Bearer <bearer auth token>" \
-H "Content-Type: application/json" \
-d '{
  "endpointMethod": "POST",
  "endpointPath": "/customers",
  "hookType": "replace",
  "validationSchema": "createCustomerSchema",
  "name": "Salesforce OCAPI customer create",
  "enabled": true,
  "config": {
    "url": "<http://localhost:3333/v2/salesforce/customer/getOne>",
    "method": "POST"
  }
}'
```

## Step-by-Step Guide to Setting Up API Hooks

### Step 1: Authenticate API Requests

Include an `Authorization` header with your API key in all requests to authenticate with the RetailOS API.

```plaintext
Authorization: API_KEY your-api-key-here
```

### Step 2: Define Your API Hook

Decide on the type of API hook you need based on your integration requirements. Use the following example payloads as a guide for setting up your hook.

### API Hook Payload Examples

#### Example 1: Replace Hook

A replace hook overrides the default RetailOS logic by forwarding requests to a specified URL and expects a POST method to handle these requests.

```json
[
  {
    "id": "your-unique-id-here",
    "endpointMethod": "POST",
    "endpointPath": "/products/search",
    "hookType": "replace",
    "validationSchema": "searchResultsSchema",
    "name": "Elasticsearch products search",
    "enabled": true,
    "config": {
      "url": "<https://your-external-service-url.com/api>",
      "method": "POST",
      "requestHeaders": {
        "Authorization": "API_KEY your-api-key-here"
      },
      "useGracefulFallback": true,
      "successfulStatusCodes": [200]
    }
  }
]
```

#### Example 2: Forward Hook

A forward hook proxies the original request to the specified URL, preserving the original request body, headers, and query parameters, except those defined in the hook's configuration.

```json
[
  {
    "id": "your-unique-id-here",
    "endpointMethod": "GET",
    "endpointPath": "/products/:id",
    "hookType": "forward",
    "validationSchema": "none",
    "name": "Your forward hook name",
    "enabled": true,
    "config": {
      "url": "<https://your-external-service-url.com/api>",
      "method": "GET",
      "requestHeaders": {
        "Authorization": "API_KEY your-api-key-here"
      }
    }
  }
]
```

### Step 3: Configure the API Hook

#### 3.1 Payload Components

- **id**: Unique identifier for the API hook (e.g., a UUID).
- **endpointMethod**: HTTP method used by the endpoint (e.g., `GET`, `POST`).
- **endpointPath**: Path of the RetailOS API endpoint that triggers the hook.
- **hookType**: Type of hook (`replace`, `after`, or `forward`).
- **validationSchema**: Specifies validation rules for the request payload.
- **name**: Descriptive name for the API hook configuration.
- **enabled**: Status flag (true for enabled, false for disabled).
- **config**: Configuration object detailing external service interaction.
  - **url**: URL of the external service to which the request is forwarded.
  - **method**: HTTP method for interacting with the external service.
  - **requestHeaders**: Headers to include in the forwarded request, such as `Authorization`.
  - **useGracefulFallback**: Indicates if the RetailOS default logic should be used on failure (for replace hooks).
  - **successfulStatusCodes**: List of HTTP status codes considered successful.

### Step 4: Test Your API Hook

1. **Simulate API Requests**: Use tools like Postman to test the configured API hook, ensuring it forwards requests as expected.
2. **Verify Responses**: Confirm that the responses from the external service are correctly processed and returned.
3. **Debug and Log**: Enable logging for API calls to monitor activity and troubleshoot any issues.

### Step 5: Deploy the API Hook

After successful testing, deploy the API hook configuration to your production environment. Monitor the integration for any performance issues or errors and make adjustments as needed.

## API Hook Configuration

### Required Configuration

- **method**: HTTP method for the webhook (currently, only `POST` is supported).
- **url**: Webhook URL where requests will be sent.

### Optional Configuration

- **requestHeaders**: Object containing key-value pairs of request headers to send with the webhook request.
- **useGracefulFallback**: Indicates whether to use graceful fallback logic on API hook failure. For replace hooks, fallback to the RetailOS controller; for after hooks, ignore on failure responses.
- **successfulStatusCodes**: Array of HTTP status codes considered successful to prevent graceful fallback. Defaults to codes >= 500.

### Example API Hook Configuration

Here's an example configuration object for an API hook:

```json
[
  {
    "id": "your-unique-id-here",
    "endpointMethod": "POST",
    "endpointPath": "/products/search",
    "hookType": "replace",
    "validationSchema": "searchResultsSchema",
    "name": "Elasticsearch products search",
    "enabled": true,
    "config": {
      "url": "<https://your-external-service-url.com/api>",
      "method": "POST",
      "requestHeaders": {
        "Authorization": "API_KEY your-api-key-here"
      },
      "useGracefulFallback": true,
      "successfulStatusCodes": [200]
    }
  }
]
```

## Bypass

API hooks can be bypassed by adding `ignoreApiHooks=true` to the request query parameters. This is useful when a replace hook needs to invoke the RetailOS controller.

**Example URL**:

```plaintext
GET <http://localhost:8080/v2/customers?ignoreApiHooks=true>
```

## Authentication and Security

- **API Key**: An API key can be included in request headers to authenticate requests from RetailOS to external systems.

## API Hook Implementation

### Content Type

RetailOS sends and expects JSON data in all requests. The following headers are included:

```plaintext
Content-Type: application/json
Accept: application/json
```

### HTTP Request Body

RetailOS sends JSON data with two main object properties:

- **incomingRequest**: Parsed HTTP request received by the RetailOS API.
- **controllerResponse**: Contains response information from RetailOS. Valid for `hookType=after`, otherwise null.

#### IncomingRequest Object

RetailOS forwards an `IncomingRequest` object to your API in the request body. This object includes:

### incomingRequest Schema

| **Field** | **Type** | **Description** |
| --- | --- | --- |
| `routePath` | `string` | Internal router path with tokens as placeholders for request params |
| `requestPath` | `string` | Full incoming request path |
| `requestMethod` | `string` | HTTP method used: `POST` `PUT` `PATCH` `GET` `DELETE` |
| `body` | `object` | For `POST` `PUT` `PATCH` requests. The schema for this will depend on the resource. |
| `params` | `object` | URL parameters included in original request. Eg:<br>```json
// Example for GET /v2/orders/:id
{
  "query": {
    "id": "123456"
  }
}
``` |
| `headers` | `object` | Headers present in original request: Eg:<br>```
{
  "headers": {
    {
      "authorization": "",
      "content-type": "application/json",
      "user-agent": "PostmanRuntime/7.32.3",
      "accept": "*/*",
      "postman-token": "c6e1c346-a800-4e01-9faf-93d47c8c8146",
      "host": "localhost:3000",
      "accept-encoding": "gzip, deflate, br",
      "connection": "keep-alive",
      "content-length": "1831"
    }
  }
}
``` |
| `query` | `object` | Query parameters from the original request. Eg:<br>```json
// Example for GET /v2/orders?limit=100
{
  "query": {
    "limit": "100"
  }
}
``` |
| `meta` | `object` | Meta information on the original request.<br>```
{
  "meta": {
      "requestId": "caeaed48-09c9-4498-aa77-d41b7fda2c3b",
      "requestTimestamp": "2023-07-28T10:41:57.060Z",
      "serverVersion": "2.716.86"
  }
}
``` |


**Example IncomingRequest Object**:

```json
{
  "routePath": "/customers/:id",
  "requestPath": "/customers/7538051",
  "requestMethod": "GET",
  "body": {},
  "params": { "id": "7538051" },
  "headers": {
    "host": "localhost:3000",
    "accept": "application/json",
    "authorization": "Bearer <bearer auth token>"
  },
  "query": {},
  "queryContext": {
    "filter": {},
    "pagination": { "offset": 0, "limit": 50 },
    "sort": { "direction": "DESC", "field": "updatedAt" }


```json
  "fields": [],
  "includes": []
},
  "meta": {
    "requestId": "fd1063f6-f59a-472d-a6d3-7907652f0ff9",
    "requestTimestamp": "2020-07-08T10:00:29.748Z",
    "serverVersion": "2.288.0"
  },
  "auth": {
    "user": {
      "catalogue": "example-product-catalogue",
      "id": "978a7d05-080d-4af1-8bce-ebc4340bf5cc",
      "username": "john.doe",
      "email": "john.doe@example.com"
    },
    "role": { "id": "704894fb-cb1b-4ff0-8c58-b394696ac40f", "name": "Admin" }
  }
}
```

### ControllerResponse Object

For **after hooks**, RetailOS includes the `controllerResponse` in the request body. `controllerResponse` will only be populated for “after” hooks, otherwise it’ll be `null`

| **Field** | **Type** | **Description** |
| --- | --- | --- |
| `isControllerResponse` | `boolean` | Is the response a response form a RetailOS API controller |
| `message` | `string` | Success message |
| `statusCode` | `number` | HTTP Status Code |
| `result` | `object` | The payload returned by RetailOS. This will vary depending on the resource. |
| `meta` | `object` | Meta information from the original request. |

#### Successful Responses

```json
{
  "isControllerResponse": true,
  "message": "Customer",
  "statusCode": 200,
  "result": {
    "id": "58c244ce-20bd-4e6c-ad5d-3aa5c0736660",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@test.com"
  }
}
```

#### Error Responses

```json
{
  "message": "Customer not found.",
  "isControllerException": true,
  "code": "CUSTOMER_NOT_FOUND",
  "statusCode": 404,
  "details": {}
}
```

## Error Handling

By default, RetailOS forwards errors from API hooks to the originating caller. Any status codes >= 500 are considered errors. A failure to make a request to the remote URL is also treated as an error.

### Configuring Error Behavior

Set the `successfulStatusCodes` in the API hook config to specify which status codes are considered successful, such as 200 or 404. For example:

```json
{
  "name": "Salesforce OCAPI get product by ID",
  "config": {
    "successfulStatusCodes": [200, 404]
  }
}
```

## Graceful Fallback

### Replace Hooks

If `useGracefulFallback=true` and the response is not successful, RetailOS will execute its default API controller. This is useful for "more advanced" functionality like Elasticsearch searches.

**Example**:

```json
{
  "name": "Elasticsearch Products search",
  "config": {
    "useGracefulFallback": true
  }
}
```

### After Hooks

For after hooks, setting `useGracefulFallback=true` allows RetailOS to ignore the after hook if the response is not successful.

**Example**:

```json
{
  "name": "Add stock information to product search",
  "config": {
    "useGracefulFallback": true
  }
}
```

## Example Implementation

Here is an example of an endpoint implementation using Node.js and Express:

```javascript
const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());

app.post('/v2/salesforce/customer/getOne', (req, res, next) => {
  const { incomingRequest, controllerResponse } = req.body;

  // Your business logic here
  myCustomerDb.getCustomerById(incomingRequest.params.id)
    .then((customer) => {
      if (customer !== null) {
        // Send a 2xx response for success
        res.status(200).json({ error: null, result: customer });
      } else {
        // Send a 4xx response for error
        const errObj = {
          code: 'CUSTOMER_NOT_FOUND',
          message: 'Customer not found',
          details: null
        };
        res.status(404).json({ error: errObj, result: null });
      }
    })
    .catch(error => next(error));
});

app.listen(8080, (err) => {
  if (err) { throw err; }

  console.log('Express server is listening on port 8080');
});
```

## Conclusion

RetailOS API hooks provide a powerful way to customize and extend the platform's functionality by integrating with external systems. By following this guide, you can effectively configure and implement API hooks to enhance your business workflows.

---

### Next Steps

- **[4.4.3 Examples](#)**: Explore practical examples of API hooks in action and discover best practices for their implementation.