From c010594447eba4493d3256ccaccaef5bfc0b15e9 Mon Sep 17 00:00:00 2001 From: Vadim Savilov Date: Fri, 17 Jul 2026 15:45:18 +0500 Subject: [PATCH] =?UTF-8?q?feature/SDK-72:=20SDK-72:=20=D0=9E=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D1=82=D1=8C=20=D1=81=D0=BA=D0=B8=D0=BB=D1=8B=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20SDK-=D1=88=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- skills/greenapi-whatsapp-java/SKILL.md | 160 ++++++++++++++++++ .../references/account-service.md | 60 +++++++ .../references/groups-contacts-statuses.md | 57 +++++++ .../references/receiving.md | 92 ++++++++++ .../references/sending.md | 79 +++++++++ 5 files changed, 448 insertions(+) create mode 100644 skills/greenapi-whatsapp-java/SKILL.md create mode 100644 skills/greenapi-whatsapp-java/references/account-service.md create mode 100644 skills/greenapi-whatsapp-java/references/groups-contacts-statuses.md create mode 100644 skills/greenapi-whatsapp-java/references/receiving.md create mode 100644 skills/greenapi-whatsapp-java/references/sending.md diff --git a/skills/greenapi-whatsapp-java/SKILL.md b/skills/greenapi-whatsapp-java/SKILL.md new file mode 100644 index 0000000..f809f98 --- /dev/null +++ b/skills/greenapi-whatsapp-java/SKILL.md @@ -0,0 +1,160 @@ +--- +name: greenapi-whatsapp-java +version: 1.0.0 +description: Write correct Java code with the GREEN-API WhatsApp SDK (com.green-api:whatsapp-api-client-java). Use when sending/receiving WhatsApp messages, files, polls, statuses, or managing groups/contacts/instance settings via GREEN-API in a Java or Spring Boot project. Covers client initialization, method groups, chatId formats, notification handling, and common pitfalls. +--- + +# GREEN-API WhatsApp Java SDK + +Library `com.green-api:whatsapp-api-client-java` — a Java wrapper over the GREEN-API REST API +(https://green-api.com/en/docs/api/). Requires an **instanceId** and **apiTokenInstance** from +https://console.green-api.com/, and the instance's WhatsApp account must be **authorized** +(QR code in the console, or `account.getAuthorizationCode(phoneNumber)`). + +## When to use this skill + +Any task that sends or receives WhatsApp messages through GREEN-API from Java: bots, +notification services, group management, polling or webhook-based message intake. + +## Installation + +Maven (the SDK is built on Spring Boot 3.x — Java 17+; it pulls `spring-boot-starter-web`): + +```xml + + com.green-api + whatsapp-api-client-java + [0.2.2,) + +``` + +## Client initialization + +Entry point: `com.greenapi.client.pkg.api.GreenApi`. It exposes public fields, one per method +group: `account`, `contacts`, `sending`, `journals`, `groups`, `queues`, `marking`, +`receiving`, `service`, `statuses`. + +**Constructor argument order is a common trap: media host comes BEFORE api host.** + +```java +import com.greenapi.client.pkg.api.GreenApi; +import org.springframework.web.client.RestTemplate; + +var greenApi = new GreenApi( + new RestTemplate(), + "https://media.green-api.com", // hostMedia — FIRST + "https://api.green-api.com", // host — SECOND + System.getenv("GREEN_API_ID_INSTANCE"), + System.getenv("GREEN_API_TOKEN")); +``` + +In Spring Boot you can instead define properties `green-api.host`, `green-api.hostMedia`, +`green-api.instanceId`, `green-api.token`, provide a `RestTemplate` bean, and add +`com.greenapi.client` to `@ComponentScan` — `GreenApi`, `WebhookConsumer` and +`NotificationMapper` are `@Component`s. + +Every SDK method returns Spring's `ResponseEntity`; call `.getBody()` for the payload and +`.getStatusCode()` to check success. Request objects are built with Lombok builders. + +## Core scenarios + +### Check the instance is authorized (do this first) + +```java +var state = greenApi.account.getStateInstance().getBody(); +// state.getStateInstance() must be "authorized"; if "notAuthorized" — scan QR in console +``` + +### Send a text message + +```java +import com.greenapi.client.pkg.models.request.OutgoingMessage; + +var resp = greenApi.sending.sendMessage(OutgoingMessage.builder() + .chatId("79876543210@c.us") // personal chat; groups: "...@g.us" + .message("Hello!") // max 20000 chars, UTF-8 + .build()); +if (resp.getStatusCode().is2xxSuccessful()) { + System.out.println("id: " + resp.getBody().getIdMessage()); +} +``` + +### Send a file + +```java +import com.greenapi.client.pkg.models.request.OutgoingFileByUrl; +import com.greenapi.client.pkg.models.request.OutgoingFileByUpload; + +// by URL (max 100 MB; fileName MUST include the extension) +greenApi.sending.sendFileByUrl(OutgoingFileByUrl.builder() + .chatId("79876543210@c.us") + .urlFile("https://example.com/report.pdf") + .fileName("report.pdf") + .caption("caption, max 1024 chars") + .build()); + +// by upload from disk +greenApi.sending.sendFileByUpload(OutgoingFileByUpload.builder() + .chatId("79876543210@c.us") + .file(new java.io.File("/path/report.pdf")) + .fileName("report.pdf") + .build()); +``` + +### Receive notifications — polling loop + +Preferred: the built-in consumer (blocking loop; it deletes each notification after your +handler runs): + +```java +import com.greenapi.client.pkg.api.webhook.WebhookConsumer; +import com.greenapi.client.pkg.api.webhook.NotificationMapper; +import com.greenapi.client.pkg.models.notifications.TextMessageWebhook; + +var consumer = new WebhookConsumer(greenApi, new NotificationMapper()); +consumer.start(notification -> { // WebhookHandler lambda + if (notification.getBody() instanceof TextMessageWebhook text) { + System.out.println(text.getMessageData().getTextMessageData().getTextMessage()); + } +}); +// consumer.stop() from another thread to exit the loop +``` + +Manual polling uses `greenApi.receiving.receiveNotification()` (long-poll, ~5 s wait; body is +a raw JSON `String`, literally `"null"` when the queue is empty) and **must** call +`greenApi.receiving.deleteNotification(receiptId)` after processing — otherwise the same +notification is redelivered. The queue keeps notifications for 24 h. + +**Polling does not work while a webhook URL is set on the instance** — clear `webhookUrl` in +instance settings first. To receive via your own HTTP webhook endpoint instead, set +`webhookUrl` in the console and parse incoming POST JSON with `NotificationMapper.get(json)`. + +## Pitfalls checklist + +- **chatId format**: personal `"@c.us"` (digits only, no `+`), group + `"@g.us"`. But `service.checkWhatsapp(Long)` and `account.getAuthorizationCode(Long)` + take a bare `Long` phone number, not a chatId. +- **Constructor order**: `GreenApi(restTemplate, hostMedia, host, instanceId, token)`. +- **Authorization**: unauthorized instance queues messages (up to 24 h) instead of sending; + verify `getStateInstance()` == `"authorized"`. +- **Send rate**: GREEN-API queues outgoing messages server-side; pace bulk sends with the + `delaySendMessagesMilliseconds` instance setting (min 500 ms) rather than client-side + hammering. Mass messaging to unknown numbers risks a WhatsApp ban. +- **`createGroup` with a non-existent number can get the sender's number banned by WhatsApp.** +- Status request classes are spelled `...Resq` in this SDK (`SendTextStatusResq`, + `SendVoiceStatusResq`, `SendMediaStatusResq`) — not `Req`. Account settings setter is + `account.setSetting(...)` (singular). Settings apply within ~5 min and reboot the instance. +- `sendFileByUrl`: URL must be a direct file link, `http(s)://`, no spaces or `~$€%#£?!`. + +## Method reference (only methods that exist in the SDK) + +- [references/sending.md](references/sending.md) — `sending.*`: messages, files, buttons, + lists, polls, location, contact, forwarding. +- [references/receiving.md](references/receiving.md) — `receiving.*`, `WebhookConsumer`, + all `*Webhook` notification classes and `typeWebhook` mapping. +- [references/account-service.md](references/account-service.md) — `account.*`, `service.*`, + `marking.*`, `queues.*`. +- [references/groups-contacts-statuses.md](references/groups-contacts-statuses.md) — + `groups.*`, `contacts.*`, `statuses.*`, `journals.*`. + +Do not invent methods not listed in these files — the SDK implements only these. diff --git a/skills/greenapi-whatsapp-java/references/account-service.md b/skills/greenapi-whatsapp-java/references/account-service.md new file mode 100644 index 0000000..36b68e3 --- /dev/null +++ b/skills/greenapi-whatsapp-java/references/account-service.md @@ -0,0 +1,60 @@ +# Account, service, marking and queue methods + +All return `ResponseEntity`; DTOs in `com.greenapi.client.pkg.models.request`, responses +in `...models.response`. + +## `greenApi.account.*` (class `GreenApiAccount`) + +| Method | Docs | Notes | +|---|---|---| +| `getSettings()` → `Settings` | api/account/GetSettings/ | current instance settings | +| `getWaSettings()` → `WaSettings` | api/account/GetWaSettings/ | WhatsApp account info (phone, avatar, state) | +| `setSetting(InstanceSettingsReq)` → `SetSettingsResp` | api/account/SetSettings/ | **name is singular `setSetting`**; only non-null fields are sent; applies within ~5 min and reboots the instance | +| `getStateInstance()` → `StateInstanceResp` | api/account/GetStateInstance/ | `getStateInstance()` string: `authorized`, `notAuthorized`, `blocked`, `starting`, `yellowCard` | +| `getStatusInstance()` → `StatusInstanceResp` | api/account/GetStatusInstance/ | socket connection status: `online`/`offline` | +| `reboot()` → `RebootResp` | api/account/Reboot/ | | +| `logout()` → `LogoutResp` | api/account/Logout/ | un-authorizes the instance | +| `getQrCode()` → `Qr` | api/account/QR/ | QR for authorization (poll ~once/sec; QR lifetime 20 s) | +| `getAuthorizationCode(Long phoneNumber)` → `GetAuthorizationCodeResp` | api/account/GetAuthorizationCode/ | phone as Long, e.g. `79876543210L` | +| `setProfilePicture(java.io.File)` → `SetProfilePictureResp` | api/account/SetProfilePicture/ | JPEG | +| `getStateInstanceHistory()` / `getStateInstanceHistory(Integer count)` → `List` | docs/api/account/GetStateInstanceHistory/ | | +| `updateApiToken()` → `UpdateApiTokenResp` | docs/api/account/UpdateApiToken/ | old token stops working — store the new one | + +`InstanceSettingsReq` notable builder fields (all optional; webhook toggles take `"yes"`/`"no"` +strings): `webhookUrl`, `webhookUrlToken`, `delaySendMessagesMilliseconds` (Integer, min 500), +`markIncomingMessagesReaded`, `markIncomingMessagesReadedOnReply`, `outgoingWebhook`, +`outgoingMessageWebhook`, `outgoingAPIMessageWebhook`, `incomingWebhook`, `stateWebhook`, +`pollMessageWebhook`, `incomingCallWebhook`, `editedMessageWebhook`, `deletedMessageWebhook`, +`incomingBlockWebhook`, `keepOnlineStatus`, `autoTyping` (Integer), `linkPreview` (Boolean), +`enableLidMode` (Boolean). + +## `greenApi.service.*` (class `GreenApiService`) + +| Method | Docs | Notes | +|---|---|---| +| `checkWhatsapp(Long phoneNumber)` → `CheckWhatsAppResp` | api/service/CheckWhatsapp/ | bare Long, **not** a chatId; `getExistsWhatsapp()` | +| `getAvatar(String chatId)` → `GetAvatarResp` | api/service/GetAvatar/ | | +| `getContacts()` → `List` | api/service/GetContacts/ | account's contact/chat list | +| `getContactInfo(String chatId)` → `GetContactInfoResp` | api/service/GetContactInfo/ | | +| `deleteMessage(DeleteMessageReq)` / `deleteMessage(MessageReq)` → `String` | api/service/deleteMessage/ | `DeleteMessageReq`: `chatId`, `idMessage`, `onlySenderDelete` (Boolean) | +| `editMessage(EditMessageReq)` → `String` | docs/api/service/EditMessage/ | `chatId`, `idMessage`, `message` (new text) | +| `archiveChat(String chatId)` / `unarchiveChat(String chatId)` → `String` | api/service/archiveChat/ | | +| `sendTyping(SendTypingReq)` → `Void` | docs/api/service/SendTyping/ | `chatId`, `typingTime` (ms), `typingType` (`"typing"`/`"recording"`) | +| `getChats()` / `getChats(Integer count)` → `List` | docs/api/service/GetChats/ | | +| `setDisappearingChat(String chatId, Long ephemeralExpiration)` → `SetDisappearingChatResp` | api/service/SetDisappearingChat/ | seconds: 0 (off), 86400, 604800, 7776000 only | + +## `greenApi.marking.*` (class `GreenApiMarking`) + +| Method | Docs | Notes | +|---|---|---| +| `readChat(MessageReq)` → `ReadChatResp` | api/marks/ReadChat/ | `chatId` required; `idMessage` null ⇒ mark whole chat read | + +## `greenApi.queues.*` (class `GreenApiQueues`) + +| Method | Docs | Notes | +|---|---|---| +| `showMessagesQueue()` → `List` | api/queues/ShowMessagesQueue/ | messages waiting to be sent | +| `clearMessagesQueue()` → `ClearMessagesQueueResp` | api/queues/ClearMessagesQueue/ | | + +Doc URLs are relative to `https://green-api.com/en/` (e.g. +`https://green-api.com/en/docs/api/service/CheckWhatsapp/`). diff --git a/skills/greenapi-whatsapp-java/references/groups-contacts-statuses.md b/skills/greenapi-whatsapp-java/references/groups-contacts-statuses.md new file mode 100644 index 0000000..071ddb2 --- /dev/null +++ b/skills/greenapi-whatsapp-java/references/groups-contacts-statuses.md @@ -0,0 +1,57 @@ +# Groups, contacts, statuses and journals + +All return `ResponseEntity`. Doc URLs relative to `https://green-api.com/en/`. + +## `greenApi.groups.*` (class `GreenApiGroups`) + +> WARNING (official docs): creating a group with, or inviting, a non-existent number can get +> the sender's number banned by WhatsApp. Group ids end with `@g.us`; participant ids with +> `@c.us`. + +| Method | Docs | Request fields | +|---|---|---| +| `createGroup(CreateGroupReq)` → `CreateGroupResp` | api/groups/CreateGroup/ | `groupName`, `chatIds` (`List` of `...@c.us`); response: `isCreated()`, `getChatId()` (new `...@g.us`) | +| `updateGroupName(ChangeGroupNameReq)` → `ChangeGroupNameResp` | api/groups/UpdateGroupName/ | `groupId`, `groupName` | +| `getGroupData(String groupId)` → `GroupData` | api/groups/GetGroupData/ | participants, owner, invite link | +| `addGroupParticipant(ChangeParticipantReq)` → `AddGroupParticipantResp` | api/groups/AddGroupParticipant/ | `groupId`, `participantChatId` | +| `removeGroupParticipant(ChangeParticipantReq)` → `RemoveGroupParticipantResp` | api/groups/RemoveGroupParticipant/ | same | +| `setGroupAdmin(ChangeParticipantReq)` → `SetGroupAdminResp` | api/groups/SetGroupAdmin/ | same | +| `removeGroupAdmin(ChangeParticipantReq)` → `RemoveGroupAdminResp` | api/groups/RemoveAdmin/ | same | +| `setGroupPicture(ChangeGroupPictureReq)` → `SetGroupPictureResp` | api/groups/SetGroupPicture/ | `groupId`, `file` (JPEG `java.io.File`) | +| `updateGroupSettings(UpdateGroupSettingsReq)` → `UpdateGroupSettingsResp` | docs/api/groups/UpdateGroupSettings/ | `groupId`, `allowParticipantsEditGroupSettings` (Boolean), `allowParticipantsSendMessages` (Boolean) | +| `leaveGroup(String groupId)` → `LeaveGroupResp` | api/groups/LeaveGroup/ | | + +## `greenApi.contacts.*` (class `GreenApiContacts`) + +| Method | Docs | Request fields | +|---|---|---| +| `addContact(AddContactReq)` → `AddContactResp` | docs/api/contacts/AddContact/ | `chatId`, `firstName`, `lastName`, `saveInAddressbook` (Boolean) | +| `editContact(EditContactReq)` → `EditContactResp` | docs/api/contacts/EditContact/ | same fields | +| `deleteContact(DeleteContactReq)` → `DeleteContactResp` | docs/api/contacts/DeleteContact/ | `chatId` | + +## `greenApi.statuses.*` (class `GreenApiStatuses`) — WhatsApp Status (stories) + +**Request class names are spelled `...Resq` in this SDK** (typo preserved for compatibility). +`participants` (optional `List` of `...@c.us`) limits who sees the status; omitted ⇒ +all contacts who have the sender in their address book. + +| Method | Docs | Request fields | +|---|---|---| +| `sendTextStatus(SendTextStatusResq)` → `SendMessageResp` | api/statuses/SendTextStatus/ | `message`, `backgroundColor` (hex e.g. `"#87CEEB"`), `font` (`SERIF`, `SANS_SERIF`, `NORICAN_REGULAR`, `BRYNDAN_WRITE`, `OSWALD_HEAVY`), `participants` | +| `sendVoiceStatus(SendVoiceStatusResq)` → `SendMessageResp` | api/statuses/SendVoiceStatus/ | `urlFile`, `fileName` (audio with extension), `backgroundColor`, `participants` | +| `sendMediaStatus(SendMediaStatusResq)` → `SendMessageResp` | api/statuses/SendMediaStatus/ | `urlFile`, `fileName` (image/video with extension), `caption`, `participants` | +| `getIncomingStatuses()` / `(Integer minutes)` → `List` | api/statuses/GetIncomingStatuses/ | default last 24 h | +| `getOutgoingStatuses()` / `(Integer minutes)` → `List` | api/statuses/GetOutgoingStatuses/ | default last 24 h | +| `getStatusStatistic(String idMessage)` → `List` | api/statuses/GetStatusStatistic/ | sent/delivered/read recipients | +| `deleteStatus(DeleteStatusReq)` → `Void` | docs/api/statuses/DeleteStatus/ | `idMessage` | + +## `greenApi.journals.*` (class `GreenApiJournals`) + +| Method | Docs | Notes | +|---|---|---| +| `getChatHistory(GetChatHistoryReq)` → `List` | api/journals/GetChatHistory/ | `chatId`, `count` (default 100) | +| `getMessage(MessageReq)` → `ChatMessage` | api/journals/GetMessage/ | `chatId`, `idMessage` | +| `lastIncomingMessages(Integer minutes)` → `List` | api/journals/LastIncomingMessages/ | default 1440 min (24 h) | +| `lastOutgoingMessages(Integer minutes)` → `List` | api/journals/LastOutgoingMessages/ | default 24 h | +| `lastIncomingCalls()` / `(Integer minutes)` → `List` | docs/api/journals/LastIncomingCalls/ | | +| `lastOutgoingCalls()` / `(Integer minutes)` → `List` | docs/api/journals/LastOutgoingCalls/ | | diff --git a/skills/greenapi-whatsapp-java/references/receiving.md b/skills/greenapi-whatsapp-java/references/receiving.md new file mode 100644 index 0000000..1929ea5 --- /dev/null +++ b/skills/greenapi-whatsapp-java/references/receiving.md @@ -0,0 +1,92 @@ +# Receiving notifications — `greenApi.receiving.*` and the webhook layer + +## HTTP polling methods (class `GreenApiReceiving`) + +### receiveNotification() → ResponseEntity +Docs: https://green-api.com/en/docs/api/receiving/technology-http-api/ReceiveNotification/ +Long-polls the notification queue (server waits ~5 s; returns literal string `"null"` if +empty). The JSON contains `receiptId` and `body`. Queue is FIFO, notifications are kept 24 h. +**Does not work while a webhook URL is configured on the instance** — clear it and wait ~1 min. + +### deleteNotification(Integer receiptId) → ResponseEntity +Docs: https://green-api.com/en/docs/api/receiving/technology-http-api/DeleteNotification/ +Must be called after processing each notification, otherwise it is redelivered. + +### downloadFile(MessageReq) → ResponseEntity +Docs: https://green-api.com/en/docs/api/receiving/files/DownloadFile/ +`MessageReq` builder: `chatId`, `idMessage`. Returns the file bytes of an incoming/outgoing +media message. (For incoming media you can also use the `downloadUrl` field from the +`FileMessageWebhook` payload.) + +## WebhookConsumer — ready-made polling loop + +Package `com.greenapi.client.pkg.api.webhook`. Classes: `WebhookConsumer`, `WebhookHandler` +(functional interface `void handle(Notification notification)`), `NotificationMapper`. + +```java +var consumer = new WebhookConsumer(greenApi, new NotificationMapper()); +consumer.start(notification -> { /* runs for each notification */ }); +// blocking loop; call consumer.stop() from another thread to end it +``` + +`start()` receives, maps JSON → typed `Notification`, calls your handler, then deletes the +notification. On mapping failure it deletes and continues; on unexpected errors it sleeps 5 s +and retries. + +For a push-style webhook endpoint (your own HTTP server registered as `webhookUrl` in +instance settings), reuse `NotificationMapper.get(jsonString)` to parse the POSTed JSON into +the same `Notification` type. The mapper handles both queue format (`{receiptId, body}`) and +raw webhook format (`{typeWebhook, ...}`). + +## Notification model + +`com.greenapi.client.pkg.models.notifications.Notification`: +- `getReceiptId()` — Integer (null for direct webhook JSON) +- `getBody()` — `NotificationBody` (abstract; check concrete subclass with `instanceof`). + `getBody().getTypeWebhook()` returns the raw type string. + +### typeWebhook → Java class (all in `com.greenapi.client.pkg.models.notifications`) + +| typeWebhook | Class | +|---|---| +| `outgoingMessageStatus` | `OutgoingMessageStatus` | +| `stateInstanceChanged` / `statusInstanceChanged` | `StatusInstanceChanged` / `StateInstanceChanged` | +| `deviceInfo` | `DeviceInfo` | +| `incomingCall` | `IncomingCall` | +| `incomingBlock` | `IncomingBlock` | + +For `incomingMessageReceived` / `outgoingMessageReceived` / `outgoingAPIMessageReceived` the +class is chosen by `messageData.typeMessage`: + +| typeMessage | Class | +|---|---| +| `textMessage` | `TextMessageWebhook` | +| `extendedTextMessage` | `ExtendedTextMessageWebhook` | +| `quotedMessage` | `QuotedMessageWebhook` | +| `imageMessage`, `videoMessage`, `documentMessage`, `audioMessage` | `FileMessageWebhook` | +| `locationMessage` | `LocationMessageWebhook` | +| `contactMessage` / `contactsArrayMessage` | `ContactMessageWebhook` / `ContactsArrayMessageWebhook` | +| `stickerMessage` | `StickerMessageWebhook` | +| `reactionMessage` | `ReactionMessageWebhook` | +| `pollMessage` / `pollUpdateMessage` | `PollMessageWebhook` / `PollUpdateMessageWebhook` | +| `listMessage` / `listResponseMessage` | `ListMessageWebhook` / `ListResponseMessageWebhook` | +| `buttonsMessage` / `buttonsResponseMessage` | `ButtonsMessageWebhook` / `ButtonsResponseMessageWebhook` | +| `templateMessage` / `templateButtonsReplyMessage` | `TemplateMessageWebhook` / `TemplateButtonsReplyMessageWebhook` | +| `groupInviteMessage` | `GroupInviteMessageWebhook` | + +Notification payload format docs: +https://green-api.com/en/docs/api/receiving/notifications-format/type-webhook/ + +Typical echo-bot handler: + +```java +consumer.start(n -> { + if (n.getBody() instanceof TextMessageWebhook t + && "incomingMessageReceived".equals(t.getTypeWebhook())) { + var chatId = t.getSenderData().getChatId(); + var text = t.getMessageData().getTextMessageData().getTextMessage(); + greenApi.sending.sendMessage( + OutgoingMessage.builder().chatId(chatId).message(text).build()); + } +}); +``` diff --git a/skills/greenapi-whatsapp-java/references/sending.md b/skills/greenapi-whatsapp-java/references/sending.md new file mode 100644 index 0000000..50dea75 --- /dev/null +++ b/skills/greenapi-whatsapp-java/references/sending.md @@ -0,0 +1,79 @@ +# Sending methods — `greenApi.sending.*` + +Class: `com.greenapi.client.pkg.api.methods.GreenApiSending`. +All request DTOs live in `com.greenapi.client.pkg.models.request`, responses in +`...models.response`. All methods return `ResponseEntity`. + +Chat-targeted DTOs extending `Outgoing` share two builder fields: `chatId` (required) and +`quotedMessageId` (optional — id of a message to quote/reply to). + +## sendMessage(OutgoingMessage) → SendMessageResp +Text message to a personal or group chat. Docs: https://green-api.com/en/docs/api/sending/SendMessage/ +`OutgoingMessage` builder fields: +- `chatId` (required) — `"79876543210@c.us"` / `"...@g.us"` +- `message` (required) — text, max 20000 chars, UTF-8, emoji OK +- `quotedMessageId`, `linkPreview` (Boolean, default true), `typePreview` (`"large"`/`"small"`), + `customPreview` (`CustomPreview`: title, description, link, urlFile, jpegThumbnail), + `typingTime` (Integer, 1000–20000 ms — shows typing indicator first) + +Response: `SendMessageResp.getIdMessage()`. + +## sendFileByUrl(OutgoingFileByUrl) → SendMessageResp +Docs: https://green-api.com/en/docs/api/sending/SendFileByUrl/ +Fields: `chatId`, `urlFile` (direct http(s) link to the file, no spaces/special chars), +`fileName` (**must contain the extension** — drives file-type detection), `caption` +(max 1024 chars), `quotedMessageId`, `typingTime`, `typingType` (`"typing"`/`"recording"`). +Max file size 100 MB. Sending a file may take 1–20 s. + +## sendFileByUpload(OutgoingFileByUpload) → SendFileByUploadResp +Docs: https://green-api.com/en/docs/api/sending/SendFileByUpload/ +Same as above but with `file` (`java.io.File`) instead of `urlFile`. Uses the media host. + +## uploadFile(File) → UploadFileResp +Docs: https://green-api.com/en/docs/api/sending/UploadFile/ +Uploads to GREEN-API storage; throws `IOException`. Use `getBody().getUrlFile()` with +`sendFileByUrl`. + +## sendLocation(OutgoingLocation) → SendMessageResp +Docs: https://green-api.com/en/docs/api/sending/SendLocation/ +Fields: `chatId`, `latitude` (Double, required), `longitude` (Double, required), +`nameLocation`, `address`, `quotedMessageId`. + +## sendContact(OutgoingContact) → SendMessageResp +Docs: https://green-api.com/en/docs/api/sending/SendContact/ +Fields: `chatId`, `contact` (`com.greenapi.client.pkg.models.Contact` builder: +`phoneContact` (Long), `firstName`, `middleName`, `lastName`, `company`), `quotedMessageId`. + +## sendPoll(OutgoingPoll) → SendMessageResp +Docs: https://green-api.com/en/docs/api/sending/SendPoll/ +Fields: `chatId`, `message` (poll question), `options` +(`List`, `new Option("text")`, 2–12 unique options), +`multipleAnswers` (Boolean, default false), `quotedMessageId`. + +## forwardMessages(ForwardMessagesReq) → ForwardMessagesResp +Docs: https://green-api.com/en/docs/api/sending/ForwardMessages/ +Fields: `chatId` (destination), `chatIdFrom` (source chat), `messages` +(`List` of message ids), `typingTime`. + +## sendInteractiveButtons(OutgoingInteractiveButtons) → SendMessageResp +Docs: https://green-api.com/docs/api/sending/SendInteractiveButtons/ +Fields: `chatId`, `header`, `body`, `footer`, `buttons` (`List`). +`InteractiveButton` builder: `type` (`"copy"`/`"call"`/`"url"`), `buttonId`, `buttonText`, +plus one of `copyCode` / `phoneNumber` / `url` matching the type. + +## sendInteractiveButtonsReply(OutgoingInteractiveButtonsReply) → SendMessageResp +Docs: https://green-api.com/docs/api/sending/SendInteractiveButtonsReply/ (beta) +Fields: `chatId`, `header`, `body`, `footer`, `buttons` (`List`: +`buttonId`, `buttonText`). + +## sendButtons(OutgoingButtons), sendTemplateButtons(OutgoingTemplateButtons), sendListMessage(OutgoingListMessage) → SendMessageResp +Legacy/limited-support button messages (WhatsApp has deprecated classic buttons; prefer +`sendInteractiveButtons`). +- `OutgoingButtons`: `chatId`, `message`, `footer`, `buttons` (`List