> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.jelliu.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.jelliu.co/_mcp/server.

# Send a widget message

POST https://api.jelliu.co/widget/message
Content-Type: application/json

Sends one visitor message in a session from `POST /widget/init` and returns the agent's reply
synchronously. Operator replies typed later in the dashboard do not come back here; pick them up with
`GET /widget/poll`.

Send `application/json` for text. To attach one image (PNG, JPG, WEBP, GIF, HEIC, HEIF, AVIF, TIFF) or PDF of up
to 10 MB, send `multipart/form-data` with the file in `file` and the other fields as form fields;
`message` is still required. HEIC/AVIF/TIFF are converted and photos rotated upright before the agent
reads them. A file over 10 MB is cut off by the upload parser and currently fails with 500 rather than
the 413 body.

Credentials, origin check and per-minute limits are the same as `POST /widget/init`; see there.

**Side effects.** On the first message creates the `webchat` conversation (bound to the widget's agent)
and links it to the session; stores the visitor message and the reply; calls the language model; draws
one AI message from the workspace's monthly allowance when a model wrote the reply; if the session has
`metadata.email`, finds or creates the contact. The reply is also pushed to other open tabs of the
session over the widget WebSocket. When the workspace is out of AI messages the visitor gets a neutral
503 and the workspace owner receives an in-app notification (once per day). No webhook event is emitted
for the message itself.

**Idempotency.** Not idempotent. A retry after a timeout stores the visitor message again, generates
a second reply and spends another AI message and another unit of the daily cap. De-duplicate replies
by `message_id`.

**Access**
- **Required scope:** None; `jl_` API keys are not used. The widget credential plus a matching `x-widget-parent-origin` authorise the call.
- **Rate limit:** Widget public — 60 requests/min per IP, the widget's `rate_limit_rpm` per credential, 300 requests/min per workspace, plus a daily cap on widget messages per workspace (5,000 by default, resets at 00:00 UTC); the general limiter also counts 120 requests/min per IP. See [Rate limits](/rate-limits).
- **Plan:** Available on every plan. Replies draw from the plan's AI-message allowance.


Reference: https://developer.jelliu.co/api-reference/widgets/post-widget-message

## Request

### Query parameters

- `wid` (string, optional) — Public widget id as a query parameter. Used when neither header is present.

### Headers

- `x-widget-api-key` (string, optional) — Secret widget API key (`plaintext_api_key` from `POST /api/widgets`). Takes precedence over `x-widget-id` and `wid`. Send one of the three. Never expose it in a browser.
- `x-widget-id` (string, optional) — Public widget id, the credential the embed script uses. Used when `x-widget-api-key` is absent.
- `x-widget-parent-origin` (string, required) — Origin of the page embedding the widget. Must match one of the widget's `allowed_origins`; see `POST /widget/init`.

### Body (application/json)

This endpoint expects an object.

- `session_id` (string, required) — Session id returned by `POST /widget/init`.
- `message` (string, required) — The visitor's text, 1 to 4000 characters. Required even when a file is attached.
- `agent_name` (string, optional) — Display name the UI shows for the agent (from `branding.agent_names`); the reply is signed with it and it is saved on the session.
- `lang` (string, optional) — Primary language subtag of the visitor's UI (`es`, `en`); used as the reply language and saved on the session.
- `identity_token` (string, optional) — Signed identity used only by Jelliu's own in-app support widget. Integrations do not need it; an invalid token, or one for another workspace, is ignored.
- `file` (string, optional) — `multipart/form-data` only. One image (PNG, JPG, WEBP, GIF, HEIC, HEIF, AVIF, TIFF) or PDF, up to 10 MB.

## Response

### 200

The agent's reply.

- `data` (object, required)
  - `conversation_id` (string, required) — The `webchat` conversation this session writes to; stable for the life of the session.
  - `message_id` (string, required) — Id of the stored reply, for de-duplicating the same message arriving via poll or WebSocket.
  - `reply` (string, required) — The reply text to show the visitor.

## Errors

### 400 Bad Request Error

`session_id` not a UUID, `message` empty or over 4000 characters, or another field invalid. No field details are returned.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 401 Unauthorized Error

`Missing widget credential` or `Invalid widget API key` (see `POST /widget/init`).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 403 Forbidden Error

Widget deactivated, workspace deleted, or origin missing / invalid / not allowed (see `POST /widget/init`).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 404 Not Found Error

The session does not exist in this widget's workspace.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 413 Content Too Large Error

The attachment is still over 10 MB after preparation.

- `error` (object, required) — Why the attachment was refused, with a sentence to show the visitor.
  - `code` ("ATTACHMENT_REJECTED", required) — Always `ATTACHMENT_REJECTED`.
  - `reason` (enum, required) — Machine-readable reason. `too_large` (413): over 10 MB. `empty` (415): zero bytes. `needs_conversion` (415): a genuine image in a format that cannot be used (for example BMP). `unsupported_type` (415): not an accepted image type or PDF. `content_mismatch` (415): the bytes are not what the declared type says.
    - Allowed values: `too_large`, `empty`, `needs_conversion`, `unsupported_type`, `content_mismatch`
  - `message` (string, required) — Sentence to show the visitor (same text for `unsupported_type` and `content_mismatch`).

### 415 Unsupported Media Type Error

The attachment is empty, not an accepted type, not what its type claims, or an image that cannot be converted.

- `error` (object, required) — Why the attachment was refused, with a sentence to show the visitor.
  - `code` ("ATTACHMENT_REJECTED", required) — Always `ATTACHMENT_REJECTED`.
  - `reason` (enum, required) — Machine-readable reason. `too_large` (413): over 10 MB. `empty` (415): zero bytes. `needs_conversion` (415): a genuine image in a format that cannot be used (for example BMP). `unsupported_type` (415): not an accepted image type or PDF. `content_mismatch` (415): the bytes are not what the declared type says.
    - Allowed values: `too_large`, `empty`, `needs_conversion`, `unsupported_type`, `content_mismatch`
  - `message` (string, required) — Sentence to show the visitor (same text for `unsupported_type` and `content_mismatch`).

### 429 Too Many Requests Error

Per-minute limits (see `POST /widget/init`), or the workspace's daily widget-message cap: `Daily message limit reached for this account. Please try again tomorrow.`

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 503 Service Unavailable Error

`TEMPORARILY_UNAVAILABLE` when the workspace has no active plan or has used its monthly AI messages (the visitor sees a neutral Spanish sentence; the owner is notified), or `Rate limiter unavailable` when a limiter store is unreachable.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

## Examples

### Text message (application/json)

**Request**

```json
{
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "Hola, ¿tienen citas disponibles el sábado en la mañana?"
}
```

**Response**

```json
{
  "data": {
    "conversation_id": "b5da2f02-fa74-48ce-8d3f-1c77dabf860b",
    "message_id": "6eaab3b2-7382-4ce3-8429-9c7aaabb15ae",
    "reply": "¡Hola María! Sí, tenemos citas el sábado entre 8:00 y 12:00. ¿Qué hora te queda mejor?"
  }
}
```

**SDK Code**

```python Text message (application/json)
import requests

url = "https://api.jelliu.co/widget/message"

payload = {
    "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
    "message": "Hola, ¿tienen citas disponibles el sábado en la mañana?"
}
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Text message (application/json)
const url = 'https://api.jelliu.co/widget/message';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"session_id":"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d","message":"Hola, ¿tienen citas disponibles el sábado en la mañana?"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Text message (application/json)
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/widget/message"

	payload := strings.NewReader("{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Hola, ¿tienen citas disponibles el sábado en la mañana?\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Text message (application/json)
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/widget/message")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Hola, ¿tienen citas disponibles el sábado en la mañana?\"\n}"

response = http.request(request)
puts response.read_body
```

```java Text message (application/json)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.jelliu.co/widget/message")
  .header("Content-Type", "application/json")
  .body("{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Hola, ¿tienen citas disponibles el sábado en la mañana?\"\n}")
  .asString();
```

```php Text message (application/json)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.jelliu.co/widget/message', [
  'body' => '{
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "Hola, ¿tienen citas disponibles el sábado en la mañana?"
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Text message (application/json)
using RestSharp;

var client = new RestClient("https://api.jelliu.co/widget/message");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Hola, ¿tienen citas disponibles el sábado en la mañana?\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Text message (application/json)
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "Hola, ¿tienen citas disponibles el sábado en la mañana?"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/widget/message")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### With the display name and UI language (application/json)

**Request**

```json
{
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "¿Cuánto cuesta una limpieza dental?",
  "agent_name": "Laura",
  "lang": "es"
}
```

**Response**

```json
{
  "data": {
    "conversation_id": "b5da2f02-fa74-48ce-8d3f-1c77dabf860b",
    "message_id": "6eaab3b2-7382-4ce3-8429-9c7aaabb15ae",
    "reply": "¡Hola María! Sí, tenemos citas el sábado entre 8:00 y 12:00. ¿Qué hora te queda mejor?"
  }
}
```

**SDK Code**

```python With the display name and UI language (application/json)
import requests

url = "https://api.jelliu.co/widget/message"

payload = {
    "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
    "message": "¿Cuánto cuesta una limpieza dental?",
    "agent_name": "Laura",
    "lang": "es"
}
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript With the display name and UI language (application/json)
const url = 'https://api.jelliu.co/widget/message';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"session_id":"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d","message":"¿Cuánto cuesta una limpieza dental?","agent_name":"Laura","lang":"es"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go With the display name and UI language (application/json)
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/widget/message"

	payload := strings.NewReader("{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"¿Cuánto cuesta una limpieza dental?\",\n  \"agent_name\": \"Laura\",\n  \"lang\": \"es\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby With the display name and UI language (application/json)
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/widget/message")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"¿Cuánto cuesta una limpieza dental?\",\n  \"agent_name\": \"Laura\",\n  \"lang\": \"es\"\n}"

response = http.request(request)
puts response.read_body
```

```java With the display name and UI language (application/json)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.jelliu.co/widget/message")
  .header("Content-Type", "application/json")
  .body("{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"¿Cuánto cuesta una limpieza dental?\",\n  \"agent_name\": \"Laura\",\n  \"lang\": \"es\"\n}")
  .asString();
```

```php With the display name and UI language (application/json)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.jelliu.co/widget/message', [
  'body' => '{
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "¿Cuánto cuesta una limpieza dental?",
  "agent_name": "Laura",
  "lang": "es"
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp With the display name and UI language (application/json)
using RestSharp;

var client = new RestClient("https://api.jelliu.co/widget/message");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"¿Cuánto cuesta una limpieza dental?\",\n  \"agent_name\": \"Laura\",\n  \"lang\": \"es\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift With the display name and UI language (application/json)
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "¿Cuánto cuesta una limpieza dental?",
  "agent_name": "Laura",
  "lang": "es"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/widget/message")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Message with a photo (file sent as the `file` part) (multipart/form-data)

**Request**

```json
{
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "Te envío la foto de mi orden médica",
  "lang": "es"
}
```

**Response**

```json
{
  "data": {
    "conversation_id": "b5da2f02-fa74-48ce-8d3f-1c77dabf860b",
    "message_id": "6eaab3b2-7382-4ce3-8429-9c7aaabb15ae",
    "reply": "¡Hola María! Sí, tenemos citas el sábado entre 8:00 y 12:00. ¿Qué hora te queda mejor?"
  }
}
```

**SDK Code**

```python Message with a photo (file sent as the `file` part) (multipart/form-data)
import requests

url = "https://api.jelliu.co/widget/message"

payload = {
    "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
    "message": "Te envío la foto de mi orden médica",
    "lang": "es"
}
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Message with a photo (file sent as the `file` part) (multipart/form-data)
const url = 'https://api.jelliu.co/widget/message';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"session_id":"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d","message":"Te envío la foto de mi orden médica","lang":"es"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Message with a photo (file sent as the `file` part) (multipart/form-data)
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/widget/message"

	payload := strings.NewReader("{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Te envío la foto de mi orden médica\",\n  \"lang\": \"es\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Message with a photo (file sent as the `file` part) (multipart/form-data)
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/widget/message")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Te envío la foto de mi orden médica\",\n  \"lang\": \"es\"\n}"

response = http.request(request)
puts response.read_body
```

```java Message with a photo (file sent as the `file` part) (multipart/form-data)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.jelliu.co/widget/message")
  .header("Content-Type", "application/json")
  .body("{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Te envío la foto de mi orden médica\",\n  \"lang\": \"es\"\n}")
  .asString();
```

```php Message with a photo (file sent as the `file` part) (multipart/form-data)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.jelliu.co/widget/message', [
  'body' => '{
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "Te envío la foto de mi orden médica",
  "lang": "es"
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Message with a photo (file sent as the `file` part) (multipart/form-data)
using RestSharp;

var client = new RestClient("https://api.jelliu.co/widget/message");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"session_id\": \"b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d\",\n  \"message\": \"Te envío la foto de mi orden médica\",\n  \"lang\": \"es\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Message with a photo (file sent as the `file` part) (multipart/form-data)
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "session_id": "b3e227dd-a9ea-48ce-9afe-09c21a5d8d2d",
  "message": "Te envío la foto de mi orden médica",
  "lang": "es"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/widget/message")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```