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
14 changes: 14 additions & 0 deletions .github/workflows/publish-fork-ghcr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ jobs:
with:
submodules: recursive

- name: Read and verify application version
id: app
shell: bash
run: |
version="$(node -p "require('./package.json').version")"
echo "version=$version" >> "$GITHUB_OUTPUT"
if [[ "$GITHUB_REF" == refs/tags/v* && "$GITHUB_REF_NAME" != "v$version" ]]; then
echo "Release tag $GITHUB_REF_NAME does not match package.json v$version" >&2
exit 1
fi

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

Expand Down Expand Up @@ -56,5 +67,8 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APP_VERSION=${{ steps.app.outputs.version }}
SOURCE_REVISION=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
12 changes: 8 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ FROM node:24-alpine AS builder
RUN apk update && \
apk add --no-cache git ffmpeg wget curl bash openssl

LABEL version="2.3.1" description="Api to control whatsapp features through http requests."
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
LABEL contact="contato@evolution-api.com"

WORKDIR /evolution

COPY ./package*.json ./
Expand Down Expand Up @@ -34,11 +30,19 @@ RUN npm run build

FROM node:24-alpine AS final

ARG APP_VERSION=development
ARG SOURCE_REVISION=unknown

LABEL org.opencontainers.image.version="$APP_VERSION"
LABEL org.opencontainers.image.revision="$SOURCE_REVISION"

RUN apk update && \
apk add tzdata ffmpeg bash openssl

ENV TZ=America/Sao_Paulo
ENV DOCKER_ENV=true
ENV APP_VERSION="$APP_VERSION"
ENV SOURCE_REVISION="$SOURCE_REVISION"

WORKDIR /evolution

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ Local storage or S3/MinIO. Automatic media download from WhatsApp. Optional audi
| WhatsApp archive | [`docs/archive.md`](./docs/archive.md) |
| Local-first reads | [`docs/local-first-reads.md`](./docs/local-first-reads.md) |
| Every instance setting | [`docs/settings.md`](./docs/settings.md) |
| Delete messages for everyone | [`docs/delete-message.md`](./docs/delete-message.md) |
| Community | [evolutionfoundation.com.br/community](https://evolutionfoundation.com.br/community) |
| Docker Hub | [evoapicloud/evolution-api](https://hub.docker.com/r/evoapicloud/evolution-api) |
| Changelog | [CHANGELOG.md](./CHANGELOG.md) |
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Username-only contacts and server-issued LIDs are documented in [WhatsApp userna

The local `Message` table now distinguishes a provisional API send from WhatsApp server acceptance and recipient delivery, deduplicates future events by WhatsApp message ID, and reconciles PN/LID aliases without rewriting either identity. See [Operational message integrity](./message-archive-integrity.md) for every field, state transition, search response, migration rule, and workflow requirement.

Message revocation automatically detects connected-account messages versus group-participant messages, uses admin deletion when permitted, and waits for WhatsApp acknowledgement before reporting success. See [Delete a message for everyone](./delete-message.md) for ownership rules, permissions, request examples, response fields, and n8n mappings.

Instance restart/connect behavior and immediate multi-webhook runtime application are documented in [Runtime actions and webhook reloads](./runtime-actions-and-webhooks.md). For teaching and defense, [Defensive detection of automation disguise](./defensive-automation-detection.md) describes typo/cadence evasion signals and countermeasures without providing a production evasion engine.

Configure global defaults with `DATABASE_READ_THROUGH_ENABLED`, `DATABASE_READ_THROUGH_TTL_SECONDS`, and `DATABASE_READ_THROUGH_TTL_OVERRIDES`. Configure each instance's TTL and method overrides under **Manager → Instance → Settings → Local data cache**. A live refresh failure is returned to the caller; stale data is never substituted silently.
Expand Down
80 changes: 80 additions & 0 deletions docs/delete-message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Delete a message for everyone

Use `DELETE /chat/deleteMessageForEveryone/{instanceName}` to revoke a message from WhatsApp. The route detects whether the original message belongs to the connected account or to another group participant, chooses the strongest valid WhatsApp revoke mode, and waits for a server acknowledgement before reporting success.

## Ownership detection

The API resolves the operation in this order:

1. If the original message exists in the instance-scoped `Message` table, its stored `key.fromMe`, chat JID, and participant JID are authoritative.
2. Otherwise, an explicit `fromMe` value is used as a compatibility override.
3. Otherwise, a group request with `participant` is treated as another participant's message and uses group-admin deletion.
4. Otherwise, the message is treated as one sent by the connected account.

The response reports the selected `deletion.target` and `deletion.ownershipSource`. Clients normally should omit `fromMe` and let the API detect the correct mode.

## Permissions and limitations

- A message sent by the connected account can be revoked in a direct chat or group, subject to WhatsApp's deletion window and server policy.
- Another participant's group message can be revoked only when the connected account is a group admin. The original participant JID is required.
- WhatsApp does not permit revoking another person's incoming direct message for everyone. The API returns `CANNOT_REVOKE_INCOMING_DIRECT_MESSAGE` instead of a misleading pending success.
- The API cannot elevate a non-admin account. When group metadata identifies the connected account as a non-admin, the request returns `GROUP_ADMIN_REQUIRED`.

## Recommended request

When message storage is enabled, only the message ID and chat JID are needed:

```http
DELETE /chat/deleteMessageForEveryone/chabad-info
apikey: YOUR_GLOBAL_API_KEY
Content-Type: application/json

{
"id": "3A222167F7B463F69944",
"remoteJid": "120363043342036080@g.us"
}
```

When the original message is not stored, copy the participant identities from the `MESSAGES_UPSERT` webhook key:

```json
{
"id": "3A222167F7B463F69944",
"remoteJid": "120363043342036080@g.us",
"participant": "184353602666626@lid",
"participantAlt": "972559128260@s.whatsapp.net"
}
```

`participantAlt` helps identity resolution, but the WhatsApp revoke protobuf itself contains only `remoteJid`, `fromMe`, `id`, and `participant`. This is why retaining extra LID metadata alone did not fix incomplete revoke keys in earlier releases.

## Confirmed response

The route no longer treats Baileys's immediate `PENDING` object as proof of deletion. It listens for the outgoing revoke message's WhatsApp server acknowledgement. Only then does it update the local message state, emit `MESSAGES_DELETE`, and return success:

```json
{
"status": "SERVER_ACK",
"deletion": {
"requestedMessageId": "3A222167F7B463F69944",
"target": "GROUP_PARTICIPANT_MESSAGE",
"ownershipSource": "ARCHIVED_MESSAGE_KEY",
"submitted": true,
"serverAcknowledged": true,
"status": "SERVER_ACK"
}
}
```

If no acknowledgement arrives within ten seconds, the API returns `DELETE_NOT_ACKNOWLEDGED` and does not mark the local message as deleted.

## n8n node

In `n8n-nodes-evolution-api-en` 4.3.1 or newer, choose **Auto Detect** for **Message Ownership**. For a group message, map these webhook values when available:

- **Contact**: `{{$json.data.key.remoteJid}}`
- **Message ID**: `{{$json.data.key.id}}`
- **Original Participant JID**: `{{$json.data.key.participant}}`
- **Participant Alternate JID**: `{{$json.data.key.participantAlt}}`

The node throws an error for an unconfirmed `PENDING` response instead of returning `success: true`.
26 changes: 15 additions & 11 deletions docs/runtime-routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -7781,7 +7781,8 @@
"enum": [
true,
false
]
],
"description": "Optional ownership override. Omit it to detect ownership from the archived original message. True means the connected account sent it; false selects admin deletion for another participant’s group message."
},
"remoteJid": {
"type": "string",
Expand All @@ -7792,10 +7793,12 @@
"type": "string"
},
"participant": {
"type": "string"
"type": "string",
"description": "Original group participant JID from the webhook message key. Required for admin deletion."
},
"participantAlt": {
"type": "string"
"type": "string",
"description": "Alternate participant JID from the webhook message key, used as a fallback for LID/PN resolution."
},
"addressingMode": {
"type": "string",
Expand All @@ -7807,7 +7810,6 @@
},
"required": [
"id",
"fromMe",
"remoteJid"
],
"if": {
Expand All @@ -7832,7 +7834,7 @@
}
},
"multipart": false,
"runtimeStatus": "CREATED",
"runtimeStatus": "OK",
"operation": {
"tags": [
"Chats"
Expand All @@ -7852,8 +7854,8 @@
}
],
"responses": {
"201": {
"description": "Resource created. See the domain documentation for the operation-specific response fields.",
"200": {
"description": "Successful response. See the domain documentation for the operation-specific response fields.",
"content": {
"application/json": {
"schema": {
Expand Down Expand Up @@ -8117,7 +8119,8 @@
"enum": [
true,
false
]
],
"description": "Optional ownership override. Omit it to detect ownership from the archived original message. True means the connected account sent it; false selects admin deletion for another participant’s group message."
},
"remoteJid": {
"type": "string",
Expand All @@ -8128,10 +8131,12 @@
"type": "string"
},
"participant": {
"type": "string"
"type": "string",
"description": "Original group participant JID from the webhook message key. Required for admin deletion."
},
"participantAlt": {
"type": "string"
"type": "string",
"description": "Alternate participant JID from the webhook message key, used as a fallback for LID/PN resolution."
},
"addressingMode": {
"type": "string",
Expand All @@ -8143,7 +8148,6 @@
},
"required": [
"id",
"fromMe",
"remoteJid"
],
"if": {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "evolution-api",
"version": "5.3.0",
"version": "5.4.2",
"description": "Rest api for communication with WhatsApp",
"main": "./dist/main.js",
"type": "commonjs",
Expand Down
2 changes: 1 addition & 1 deletion src/api/dto/chat.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class ProfilePictureDto {

class Key {
id: string;
fromMe: boolean;
fromMe?: boolean;
remoteJid: string;
remoteJidAlt?: string;
participant?: string;
Expand Down
Loading
Loading