Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions skills/greenapi-whatsapp-java/SKILL.md
Original file line number Diff line number Diff line change
@@ -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
<dependency>
<groupId>com.green-api</groupId>
<artifactId>whatsapp-api-client-java</artifactId>
<version>[0.2.2,)</version>
</dependency>
```

## 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<T>`; 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 `"<countrycode+number>@c.us"` (digits only, no `+`), group
`"<id>@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.
60 changes: 60 additions & 0 deletions skills/greenapi-whatsapp-java/references/account-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Account, service, marking and queue methods

All return `ResponseEntity<T>`; 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<StateInstanceHistoryRecord>` | 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<GetContactsResp>` | 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<GetChatsResp>` | 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<QueueMessage>` | 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/`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Groups, contacts, statuses and journals

All return `ResponseEntity<T>`. 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<String>` 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<String>` 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<ChatMessage>` | api/statuses/GetIncomingStatuses/ | default last 24 h |
| `getOutgoingStatuses()` / `(Integer minutes)` → `List<ChatMessage>` | api/statuses/GetOutgoingStatuses/ | default last 24 h |
| `getStatusStatistic(String idMessage)` → `List<GetStatusStatisticResp>` | 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<ChatHistoryMessage>` | api/journals/GetChatHistory/ | `chatId`, `count` (default 100) |
| `getMessage(MessageReq)` → `ChatMessage` | api/journals/GetMessage/ | `chatId`, `idMessage` |
| `lastIncomingMessages(Integer minutes)` → `List<ChatMessage>` | api/journals/LastIncomingMessages/ | default 1440 min (24 h) |
| `lastOutgoingMessages(Integer minutes)` → `List<ChatMessage>` | api/journals/LastOutgoingMessages/ | default 24 h |
| `lastIncomingCalls()` / `(Integer minutes)` → `List<CallRecord>` | docs/api/journals/LastIncomingCalls/ | |
| `lastOutgoingCalls()` / `(Integer minutes)` → `List<OutgoingCallRecord>` | docs/api/journals/LastOutgoingCalls/ | |
Loading