> ## Documentation Index
> Fetch the complete documentation index at: https://platform.docs.zenoo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP Connector

> A built-in HTTP connector facilitates making HTTP calls directly from the DSL.

# HTTP Connector Overview

The HTTP connector enables you to make HTTP(S) requests directly from the DSL, supporting a wide range of features for integration, automation, and external API communication.

> **Related Connector Documentation**
>
> * [Connectors Overview](/technical-specification/hub-backend/connectors/overview) - Complete guide to Hub's connector system
> * [Custom Connector Development](/technical-specification/hub-backend/connectors/custom-development) - Build your own connectors
> * [Plugin Connector Development](/technical-specification/hub-backend/connectors/plugin-development) - OSGI plugin connectors

The HTTP connector can be used **standalone** for direct response handling or within an **exchange** for unified error handling and retries. Below is a summary of its main features:

* [Named HTTP Connectors](#named-http-connectors): Assign names for better logging, metrics, and troubleshooting.
* [Error Handling and Status Codes](#error-handling-and-status-codes): Fine-grained control over retries and response handling, including [Retries](#retries) and response handlers.
* [GET and POST Requests](#get-requests): Simple syntax for common HTTP methods.
* [Query Parameters](#query-parameters): Easily add dynamic query parameters with proper encoding.
* [Headers](#headers): Add custom HTTP headers for authentication, API keys, and more.
* [Request Body Types](#json-body): Support for JSON, raw, form, multipart, and JWT bodies.
* [Authorization](#authorization): Built-in support for Basic and Bearer authentication.
* [Transports](#transports): Reusable authentication and base URL configuration (Basic, Bearer/OAuth2, etc.).
* [Best Practices](#best-practices): Tips for robust and maintainable integrations.

Jump to a section for details, or read on for a full guide to all features.

### HTTP connector

A built-in HTTP connector facilitates making HTTP calls directly from the DSL.

It is possible to reference and use [context attributes](/technical-specification/hub-backend/engine/attributes) in a connector definition.
The common use-cases include URL and body generation, authentication headers, etc.

An HTTP connector response is automatically converted into a context attribute based on the *content type*.
There is a built-in support for JSON and XML content types.

## Usage Patterns

### Standalone HTTP Usage

Use HTTP directly when you need **granular control** over response handling, such as branching logic based on specific status codes:

```groovy theme={null}
// Direct HTTP usage with custom response handling
http('user-validation') {
    url 'https://api.validation.com/users'
    method 'POST'
    jsonBody user: payload.user
}.onStatus(400) {
    // Handle validation errors specifically
    decision('VALIDATION_FAILED', it)
}.onStatus([429, 503]) {
    // Handle rate limiting and service unavailable
}.onSuccess {
    // Continue with successful response
    success(it.data)
}
```

### Exchange-wrapped HTTP Usage

Use HTTP within an **exchange** for unified error handling, retries, and monitoring:

```groovy theme={null}
// HTTP within exchange for standardized handling
exchange('user-validation') {
    http {
        url 'https://api.validation.com/users'
        method 'POST'
        jsonBody user: payload.user
    }
    timeout 30
    fixedBackoffRetry {
        retry 3
        backoff 5
    }
}
```

**When to use standalone HTTP:**

* Need different handling for specific status codes (400, 401, 404, etc.)
* Complex response branching logic
* Custom retry strategies per status code
* Immediate response processing without standard error wrapping

**When to use exchange-wrapped HTTP:**

* Standard error handling and retries are sufficient
* Want consistent monitoring and metrics
* Need timeout configuration
* Prefer unified connector interface

### Named HTTP Connectors

You can assign a name to an HTTP connector invocation using the syntax:

```groovy theme={null}
http('get-clients') {
    url 'https://api.example.com/clients'
    method 'GET'
}
```

#### Why use a name?

* **Logging**: Named HTTP connectors make logs more readable and easier to search, as the name appears in execution logs.
* **Metrics**: Names are used as identifiers in metrics, making it easier to track the performance and usage of specific API calls.
* **Troubleshooting**: When errors occur, having a descriptive name helps quickly identify which external call failed.
* **Billing**: If you track API usage for billing or cost allocation, using names allows for more granular reporting.

#### Default Name

If you do not provide a name, the connector will use the request URL as the default name in logs and metrics:

```groovy theme={null}
http {
    url 'https://api.example.com/clients'
    method 'GET'
}
```

In this case, the name will be set to the value of the `url` property.

> **Tip:** Use descriptive names for important or frequently used HTTP connectors to improve observability and maintainability.

### Error Handling and Status Codes

The HTTP connector provides several ways to handle responses and errors:

#### Response Handlers

You can use response handlers to manage different outcomes of your HTTP requests:

* **onStatus(statusCode)**: Executes a block if the response status code matches the given value. Useful for handling specific error or success codes.
* **onStatus(\[statusCode1, statusCode2, ...])**: Executes a block if the response status code matches any of the values in the array. Useful for handling multiple related status codes.
* **onStatus(range)**: Executes a block if the response status code falls within the given range (e.g., `400..499` for client errors, `500..599` for server errors).
* **onResponse**: Executes a block for any response, giving you access to the full response entity (headers, status, payload).
* **onSuccess**: Executes a block if the request is successful (typically for 2xx status codes).
* **onError**: Executes a block if the request results in an error (typically for 4xx or 5xx status codes).

**Examples:**

Handle a specific status code (e.g., 400 Bad Request):

```groovy theme={null}
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onStatus(400) {
    // 'it' contains the response payload
    match(it.code_error in [1108, 2001, 3001, 3020]) {
        decision('INCORECT_CPF', it)
    }
    error(it)
}
```

Handle multiple specific status codes using an array:

```groovy theme={null}
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onStatus([502, 503, 504]) {
    // Handle server errors that might be temporary
    log "Server temporarily unavailable: ${it}"
    delay(30)
    retry()
}.onStatus([400, 401, 403]) {
    // Handle client errors that shouldn't be retried
    decision('CLIENT_ERROR', it)
}
```

Handle status code ranges:

```groovy theme={null}
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onStatus(400..499) {
    // Handle all client errors (4xx)
    log "Client error occurred: ${it.status}"
    decision('CLIENT_ERROR', it)
}.onStatus(500..599) {
    // Handle all server errors (5xx) - these might be retryable
    log "Server error occurred: ${it.status}"
    delay(10)
    retry()
}
```

Handle any response and branch on status:

```groovy theme={null}
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onResponse {
  match(it.status == 201) {
      success(it.headers.Location)
  }
  error(it.payload)
}
```

Handle all successful responses:

```groovy theme={null}
http {
  url 'https://api.example.com/data'
}.onSuccess {
  // 'it' contains the successful response
  log "Success: ${it}"
  // process success
}
```

Handle all error responses:

```groovy theme={null}
http {
  url 'https://api.example.com/data'
}.onError {
  // 'it' contains the error response
  log "Error: ${it}"
  // process error
}
```

You can chain multiple handlers for fine-grained control over error and success cases. Only the first matching handler is executed.

### GET requests

Making an HTTP GET is as simple as providing a request **url**

```groovy theme={null}
http {
    url 'https://request-url'
}
```

An *url* can be generated using Groovy [GString](http://docs.groovy-lang.org/latest/html/documentation/index.html#all-strings) and *context attributes*.

An example below queries GitHub repositories using a **keyword** attribute.

```groovy theme={null}
http {
  url "https://api.github.com/search/repositories?q=topic:${keyword}"
}
```

### Query Parameters

Query parameters can be added to HTTP requests using the **queryParam** method. This is particularly useful when you need to:

* Add dynamic parameters from context attributes
* Maintain clean, readable URL construction
* Properly encode parameter values

**Basic usage:**

```groovy theme={null}
http {
    url 'https://api.example.com/search'
    queryParam 'q', 'searchTerm'
    queryParam 'page', '1'
    queryParam 'limit', '10'
}
```

**Using context attributes:**

```groovy theme={null}
http {
    url 'https://api.example.com/users'
    queryParam 'userId', user.id
    queryParam 'status', user.status
}
```

**Dynamic parameter values with GString:**

```groovy theme={null}
http {
    url 'https://api.example.com/notify'
    queryParam 'To', "${countryCode}${phoneNumber}"
    queryParam 'Channel', 'sms'
}
```

> **Note:** Query parameters are automatically URL-encoded unless you use `disabledEncoding()`. See [URL Encoding](#url-encoding) for more details.

### Headers

The **header** method allows you to add custom HTTP headers to your request. Headers are commonly used for:

* Custom authentication tokens
* API keys
* Content negotiation
* Tracking and correlation IDs

**Basic usage:**

```groovy theme={null}
http {
    url 'https://api.example.com/data'
    header 'X-API-Key', apiKey
    header 'X-Request-ID', requestId
}
```

**Using context attributes:**

```groovy theme={null}
http {
    url 'https://api.example.com/secure'
    header 'X-Auth-Token', session.authToken
    header 'X-User-ID', user.id
}
```

**Multiple headers:**

```groovy theme={null}
http {
    url 'https://api.example.com/resource'
    header 'Accept', 'application/json'
    header 'Accept-Language', 'en-US'
    header 'X-Correlation-ID', correlationId
    method 'GET'
}
```

**Headers with multiple values:**

```groovy theme={null}
http {
    url 'https://api.example.com/resource'
    header 'Accept': ['application/json', 'application/xml']
    header 'X-Custom-Header', 'value'
}
```

**Using map syntax:**

```groovy theme={null}
http {
    url 'https://api.example.com/resource'
    header 'X-API-Key': apiKey, 'X-Client-ID': clientId
}
```

> **Tip:** For standard authentication, use the dedicated methods: `basicAuth` for Basic authentication (see [Authorization](#authorization)) or `bearerAuth` for Bearer tokens instead of setting headers manually.

> **Note:** Headers can also be configured at the [transport](#transports) level for reuse across multiple requests. Connector-level headers override transport-level headers with the same name.

### POST requests

HTTP POST request has the **method** set to `POST`.

### JSON body

The request **body** is set using the *payload* expression result. The *payload* expression can reference and use available *context attributes*.
Retryable error forces connector to try again even on 404 NOT\_FOUND response from server. This can be useful in subsequent call to easily poll for object until it is ready.

```groovy theme={null}
http {
  url "${middleware.url}/api/v1/client"
  method 'POST'
  body """whatever"""
  retryableError 404
}
```

A JSON request body is specified using **jsonBody** together with `application/json` content type.

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    jsonBody firstname: client.firstname, lastname: client.lastname
}
```

### JSON body with Base64-encoded files

It is possible to specify a JSON request body using descriptors. The corresponding files are encoded using Base64.

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    jsonBody id: [front: base64(upload.idFront), back: base64(upload.idBack)]
}
```

### HTTP body with binary data

Some API endpoints require sending binary data in HTTP body. This is supported by *rawBody*.
You can send content of a file by passing a file descriptor to it. If the parameter is a string, it's send as it is,
maps and lists are rendered as json.

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    rawBody upload.idFront
}
```

### Form data

The **formData** specifies an HTTP request using a form data using `application/x-www-form-urlencoded` content type.

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    formData 'data1', content1
    formData 'data2', content2
}
```

### Multipart data

The HTTP connector supports sending `multipart/form-data` requests with various content types using the following methods:

* **`multipartFile(String name, Attribute descriptor)`:** Adds a file part to the request. The `descriptor` should be an `Attribute` that represents a `FileDescriptor`. The content type is set according to the provided file descriptor.
* **`multipartText(String name, String content)`:** Adds a text part to the request. The content type is automatically set to `text/plain`.
* **`multipartJson(String name, Object content)`:** Adds a JSON part to the request. The `content` can be an `Attribute` or any object that can be serialized to JSON (e.g., a map, a list, or a custom object). The content type is automatically set to `application/json`.
* **`multiPart(String name, String contentType, Object value)`:** Adds a multipart entry with the specified content type. The `contentType` should be one of the following: `file` (`application/octet-stream`), `text` (`text/plain`), or `json` (`application/json`).

**Examples:**

**File Entry using `multipartFile`:**

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    multipartFile 'document', upload.idFront // upload.idFront is an Attribute containing a FileDescriptor
}
```

**Text Entry using `multipartText`:**

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    multipartText 'name', 'John Doe'
}
```

**JSON Entry using `multipartJson`:**

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    multipartJson 'metadata', [type: 'contract', version: '2.0'] // Using a map
}
```

**JSON Entry with Attribute using multipartJson:**

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    multipartJson 'metadata', myAttribute //  Attribute
}
```

**Using multiPart:**

```groovy theme={null}
// File Entry
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'file', 'file', reader.get("document") // reader.get("document") is an Attribute containing a FileDescriptor
}
```

```groovy theme={null}
// Text Entry
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'text', 'text', 'some text'
}
```

```groovy theme={null}
// JSON Entry (using an `Attribute`):
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'json', 'json', myAttribute // myAttribute is an Attribute
}
```

```groovy theme={null}
// JSON Entry (using a map):
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'json', 'json', [key: 'value'] // The map will be converted to an Attribute internally
}
```

**Mixed Content:**

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    multipartFile 'document', upload.idFront // Using multipartFile
    multipartText 'description', 'Important document' // Using multipartText
    multipartJson 'metadata', [type: 'contract', version: '2.0'] // Using multipartJson
}
```

Important Considerations:

For multipartFile, make sure the Attribute you provide actually contains a FileDescriptor.

For multipartJson, the content can be an Attribute or any object serializable to JSON.

The multiPart method provides a more generic way to add multipart entries, but it requires you to specify the contentType string explicitly.

The multiPart method infers the entry type (file, text, or json) based on the provided contentType and the type of value.

Optionally, it is possible to use a JSON builder, see [JsonBuilder](http://docs.groovy-lang.org/latest/html/gapi/groovy/json/JsonBuilder.html)

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    jsonBody {
      client {
        firstName client.firstname
        lastName client.lastname
      }
    }
}
```

### JWT in body

The request **body** is specified using **jwtBody**. The *payload* expression can reference and use available *context attributes*.
In addition to *payload* it necessary to define *secret* which is a shared secret with the other service.
*alg* is optional and defaults to `HS256`. Possible values are "HS256", "HS384" and "HS512"".

```groovy theme={null}
http {
  url "${thirdparty.url}/webhook"
  method 'POST'
  jwtBody {
      payload """whatever"""
      secret "secret"
      alg "HS512"
  }
}
```

### JWT body with Base64-encoded files

It is possible to specify a JWT request body using descriptors. The corresponding files are encoded using Base64.

```groovy theme={null}
http {
    url 'http://localhost'
    method 'POST'
    jwtBody {
        payload([front: base64(upload.idFront), back: base64(upload.idBack)])
        secret "secret"
    }
}
```

### Request method

The **method** specifies an HTTP request method. If omitted, the default method is **GET**.

The *method* can be one of the following:

* `DELETE`
* `GET`
* `HEAD`
* `OPTIONS`
* `PATCH`
* `POST`
* `PUT`
* `TRACE`

```groovy theme={null}
http {
    url "/api/files/cache/${uuid}"
    method 'DELETE'
}
```

### Content type

The **contentType** specifies an HTTP request `Content-Type` header.

```groovy theme={null}
http {
    url 'http://localhost'
    contentType 'APPLICATION_JSON_VALUE'
    method 'POST'
    body payload
}
```

### URL Encoding

Disable URL encoding in the request to avoid double encoding.

```groovy theme={null}
http {
    url 'http://localhost?param=%2Fencoded%2Bparam'
    method 'POST'
    disabledEncoding()
}
```

### Retries

By default, the HTTP connector will automatically retry requests for the following status codes:

* 408 (Request Timeout)
* 425 (Too Early)
* 429 (Too Many Requests)
* 500 (Internal Server Error)
* 502 (Bad Gateway)
* 503 (Service Unavailable)
* 504 (Gateway Timeout)

You can customize which status codes are considered retryable using the `retryableError` and `unretryableError` options:

```groovy theme={null}
http {
    url 'https://api.example.com/resource'
    retryableError 404   // Add 404 to the retryable list
    unretryableError 500 // Remove 500 from the retryable list
}
```

* Use `retryableError <code>` to add a status code to the retryable list.
* Use `unretryableError <code>` to remove a status code from the retryable list.

This allows you to fine-tune retry behavior for your specific use case.

## Authorization

### Basic authentication

The **basicAuth** specifies an HTTP basic authentication credentials.

```groovy theme={null}
http {
    url 'http://localhost'
    basicAuth 'user', 'password'
}
```

### Bearer authentication

The **bearerAuth** specifies a Bearer Authentication token used in `Authentication` HTTP header. `unretryableError` makes
connector not to retry the request on specified response status code which might become handy if there's non-standard server/gateway behaviour.

```groovy theme={null}
http {
    url "${api.url}/verify"
    bearerAuth access_token
    unretryableError 500
}
```

### Transports

A **transport** is a reusable configuration for HTTP connections, allowing you to define a base URL and authentication method (such as Basic or Bearer/OAuth2). You can reference a transport by name in your HTTP connector, making it easy to share connection settings across multiple requests.

#### Defining a Transport

Transports are defined separately and can include a base URL and authentication. Example definitions:

**Basic Authentication Transport:**

```groovy theme={null}
transport('myTransport') {
    baseUrl 'https://api.example.com'
    auth {
        basic {
            username 'user'
            password 'pass'
        }
    }
}
```

**Bearer (OAuth2) Authentication Transport:**

```groovy theme={null}
transport('myBearerTransport') {
    baseUrl 'https://api.example.com'
    auth {
        bearer {
            url 'https://auth.example.com/oauth/token'
            type 'client_credentials'  // or 'password'
            scope 'read write'         // optional
            username 'myuser'          // required for 'password' grant type
            password 'mypassword'      // required for 'password' grant type
            clientCredentials 'clientid:clientsecret'  // required for 'client_credentials' grant type
        }
    }
}
```

**Transport with Headers:**

```groovy theme={null}
transport('myTransportWithHeaders') {
    baseUrl 'https://api.example.com'
    header 'X-API-Version', '2.0'
    header 'X-Client-ID', config('client.id')
    header 'Accept', 'application/json'
}
```

**Transport with Multiple Headers and Authentication:**

```groovy theme={null}
transport('fullTransport') {
    baseUrl 'https://api.example.com'
    header 'X-API-Version', '2.0'
    header 'X-Client-ID': 'my-client-123'
    header 'Accept': ['application/json', 'application/xml']
    auth {
        bearer {
            url 'https://auth.example.com/oauth/token'
            clientCredentials config('oauth.credentials')
        }
    }
}
```

> **Note:**
>
> * The `url` can be relative to the transport's `baseUrl`
> * The authentication method defined in the transport will be applied automatically
> * You can use context attributes in transport definitions for dynamic configuration
> * The default grant type for bearer is `password` if not specified
> * Transport headers are applied as default headers to all requests using that transport
> * Headers can be defined using single values, lists, or map syntax
> * Connector-level headers override transport-level headers with the same name

#### Using a Transport in HTTP Connector

To use a transport, reference it by name in your HTTP connector:

```groovy theme={null}
http {
    transport 'myTransport'
    url '/endpoint' // relative to baseUrl
    method 'GET'
}
```

### Best Practices

1. **Error Handling**:
   * Always handle expected error cases using `onStatus` handlers
   * Use `retryableError` and `unretryableError` to customize retry behavior
   * Consider using transports for consistent error handling across multiple requests

2. **Authentication**:
   * Use transports for reusable authentication configurations
   * Keep sensitive credentials in context attributes or secure storage
   * Consider token expiration and refresh mechanisms for OAuth2

3. **Request Configuration**:
   * Use context attributes for dynamic URLs and parameters
   * Set appropriate timeouts for different types of requests
   * Use appropriate content types for different body types

4. **File Handling**:
   * Use appropriate multipart methods for file uploads
   * Consider file size limits and content types
   * Use Base64 encoding when required by the API

5. **Response Handling**:
   * Use appropriate response handlers for different status codes
   * Handle both success and error cases
   * Consider response size and content type
