| Title: | R Client for 'whapi.cloud' |
|---|---|
| Description: | Provides an R interface to the 'whapi.cloud' 'API', enabling sending and receiving 'WhatsApp' messages directly from 'R'. Functions include sending text, images, documents, stickers, geographic locations, and interactive messages (buttons and lists). Also includes webhook parsing utilities and channel health checks. |
| Authors: | Andre Leite [aut, cre], Hugo Vaconcelos [aut], Diogo Bezerra [aut] |
| Maintainer: | Andre Leite <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.0.2 |
| Built: | 2026-05-28 09:21:55 UTC |
| Source: | https://github.com/strategicprojects/whapi |
section column)Converts a tibble of services (with columns id, title, description, and section)
into a nested list of sections/rows in the format expected by Whapi interactive
messages.
whapi_build_servicos_sections(tbl, section_order = NULL)whapi_build_servicos_sections(tbl, section_order = NULL)
tbl |
A tibble/data.frame containing at least:
|
section_order |
Character vector defining desired order (and subset) of sections.
If |
If section_order = NULL, all sections are included, ordered alphabetically.
If section_order is provided, it acts as both:
a filter: only sections listed will be included,
and an order: sections appear in the same order as in section_order.
Within each section, rows are ordered by the numeric part of id
(via readr::parse_number(id)).
A list of sections, where each section is a list with:
title: section title
rows: a list of rows, each being a list with id, title, and description
de_para_servicos <- tibble::tibble( section = c("Outros Servicos","Renovacoes","Anuencia Previa"), id = c("os2","r4","ap1"), title = c("Consulta Previa", "Renovacao de Consulta Previa", "Desmembramento"), descricao = c("Initial analysis...","Renewal...","Technical authorization...") ) # All sections (alphabetical) whapi_build_servicos_sections(de_para_servicos) # Custom order and filter whapi_build_servicos_sections( de_para_servicos, section_order = c("Anuencia Previa","Outros Servicos") )de_para_servicos <- tibble::tibble( section = c("Outros Servicos","Renovacoes","Anuencia Previa"), id = c("os2","r4","ap1"), title = c("Consulta Previa", "Renovacao de Consulta Previa", "Desmembramento"), descricao = c("Initial analysis...","Renewal...","Technical authorization...") ) # All sections (alphabetical) whapi_build_servicos_sections(de_para_servicos) # Custom order and filter whapi_build_servicos_sections( de_para_servicos, section_order = c("Anuencia Previa","Outros Servicos") )
Calls GET /health to retrieve channel status, versions,
uptime, device, IP, and user info.
whapi_check_health( wakeup = TRUE, channel_type = c("web", "mobile"), token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_check_health( wakeup = TRUE, channel_type = c("web", "mobile"), token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
wakeup |
Logical(1). If TRUE, adds |
channel_type |
Character(1). Channel type, either |
token |
Character(1). Bearer token. Defaults to env var |
timeout |
Numeric(1). Request timeout (s). Default 30. |
verbose |
Logical(1). Print CLI logs? Default TRUE. |
A tibble with key health information:
channel_id, uptime, version, core_version, api_version,
device_id, ip,
status_code, status_text,
user_id, user_name, user_pushname, is_business,
profile_pic, profile_pic_full, user_status,
plus the raw response in resp.
## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") # Default check (wakeup=TRUE, channel_type="web") whapi_check_health() # Check with channel_type = "mobile" whapi_check_health(channel_type = "mobile") ## End(Not run)## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") # Default check (wakeup=TRUE, channel_type="web") whapi_check_health() # Check with channel_type = "mobile" whapi_check_health(channel_type = "mobile") ## End(Not run)
Shortens text to a maximum width (character length). If the text is longer,
it is truncated and an ellipsis (default "...") is appended.
whapi_clip_text(x, width = 420, ellipsis = "...")whapi_clip_text(x, width = 420, ellipsis = "...")
x |
Character string to clip. |
width |
Maximum length of the output including ellipsis. |
ellipsis |
Character to indicate truncation. |
A clipped string with ellipsis if needed.
whapi_clip_text("This is a very long sentence that should be clipped.", width = 20) #> "This is a very long..."whapi_clip_text("This is a very long sentence that should be clipped.", width = 20) #> "This is a very long..."
Internal helper that converts a data.frame/tibble or list of button
definitions into a normalized list-of-lists, applying a few rules:
Accepts aliases label / name and maps them to title;
Requires a non-empty title;
Auto-generates id when missing using slugification plus uniqueness
enforcement (e.g., "Buy Now" -> "buy_now", duplicates become
"buy_now_2", "buy_now_3", ...).
This is useful before building payloads for Whapi interactive endpoints (e.g., buttons, mixed actions, etc.).
whapi_coerce_buttons_base(buttons, verbose = TRUE)whapi_coerce_buttons_base(buttons, verbose = TRUE)
buttons |
A |
verbose |
Logical (default |
A list of named lists (one per button), each guaranteed to have
at least title (non-empty). If a button had no id, a slugified,
unique id is created.
whapi_slugify(), whapi_make_unique()
# From tibble (title only -> ids auto-generated) # tibble::tribble(~title, "Buy Now", "Buy Now", "Learn More") |> # whapi_coerce_buttons_base() # From list (mix with/without id) # whapi_coerce_buttons_base(list( # list(title = "Website", url = "https://example.com"), # list(title = "Website") # will get an auto id too # ))# From tibble (title only -> ids auto-generated) # tibble::tribble(~title, "Buy Now", "Buy Now", "Learn More") |> # whapi_coerce_buttons_base() # From list (mix with/without id) # whapi_coerce_buttons_base(list( # list(title = "Website", url = "https://example.com"), # list(title = "Website") # will get an auto id too # ))
Internal helper that prepares mixed action buttons for Whapi interactive
messages. It first normalizes input via whapi_coerce_buttons_base() (accepts
data.frame/tibble or list, maps aliases to title, auto-creates id
with slug + uniqueness) and then validates each button according to its
declared type:
url -> requires fields: title, id, url
call -> requires fields: title, id, phone_number
copy -> requires fields: title, id, copy_code
Enforces WhatsApp constraints: 1 to 3 buttons per message.
whapi_coerce_buttons_mixed(buttons, verbose = TRUE)whapi_coerce_buttons_mixed(buttons, verbose = TRUE)
buttons |
A |
verbose |
Logical (default |
A list-of-lists of buttons, each validated to contain the fields required
for its type.
whapi_coerce_buttons_base() for normalization;
whapi_coerce_buttons_quick() for quick-reply buttons.
# Example with a tibble: # tibble::tribble( # ~title, ~type, ~url, # "Website", "url", "https://example.com", # "Call Support","call", NA # ) |> # whapi_coerce_buttons_base() |> # whapi_coerce_buttons_mixed() # Example with a list: # whapi_coerce_buttons_mixed(list( # list(type="url", title="Website", url="https://example.com"), # list(type="call", title="Call us", phone_number="5581999999999"), # list(type="copy", title="Copy OTP", copy_code="123456") # ))# Example with a tibble: # tibble::tribble( # ~title, ~type, ~url, # "Website", "url", "https://example.com", # "Call Support","call", NA # ) |> # whapi_coerce_buttons_base() |> # whapi_coerce_buttons_mixed() # Example with a list: # whapi_coerce_buttons_mixed(list( # list(type="url", title="Website", url="https://example.com"), # list(type="call", title="Call us", phone_number="5581999999999"), # list(type="copy", title="Copy OTP", copy_code="123456") # ))
Internal helper that prepares quick reply buttons for Whapi interactive
messages. It relies on whapi_coerce_buttons_base() to normalize input
(accept data.frame/list, map aliases label/name -> title,
auto-generate id via slug + uniqueness) and then:
Enforces type = "quick_reply" for all buttons;
Requires the fields title and id;
Ensures the WhatsApp constraint of 1 to 3 buttons.
whapi_coerce_buttons_quick(buttons, verbose = TRUE)whapi_coerce_buttons_quick(buttons, verbose = TRUE)
buttons |
A |
verbose |
Logical (default |
A list-of-lists of buttons, each including at least title, id,
and type = "quick_reply".
whapi_coerce_buttons_base() for normalization; other coercers for
mixed buttons (url/call/copy).
# tibble::tribble(~title, "YES", "NO") |> # whapi_coerce_buttons_base() |> # whapi_coerce_buttons_quick()# tibble::tribble(~title, "YES", "NO") |> # whapi_coerce_buttons_base() |> # whapi_coerce_buttons_quick()
Internal helper that constructs the standard structure shared by Whapi
interactive messages (button, list, mixed actions, etc.).
It automatically normalizes the recipient (to) using
whapi_normalize_to(), and creates header, body, and footer blocks
only if the corresponding text is provided.
whapi_common_blocks(to, body_text, header_text = NULL, footer_text = NULL)whapi_common_blocks(to, body_text, header_text = NULL, footer_text = NULL)
to |
Character(1). Recipient phone number in international format
(digits only, no |
body_text |
Character(1). Main text of the interactive message body. |
header_text |
Character(1), optional. Optional header text. |
footer_text |
Character(1), optional. Optional footer text. |
Many Whapi interactive endpoints (e.g., messages/interactive) require
the same basic structure:
to: target number or chat id;
header: optional text shown above the body;
body: main message text (required);
footer: optional small text shown below the body.
This helper ensures consistency and avoids repeating boilerplate code when building different interactive message payloads.
A named list ready to be merged into a Whapi interactive message payload,
containing elements: to, header (if provided), body, and footer
(if provided).
whapi_send_quick_reply(), whapi_send_list(),
whapi_send_mixed_actions()
## Not run: # Minimal body only whapi_common_blocks("5581999999999", body_text = "Choose an option below") # With header and footer whapi_common_blocks( to = "5581999999999", body_text = "Do you confirm?", header_text = "Booking Confirmation", footer_text = "Reply now" ) ## End(Not run)## Not run: # Minimal body only whapi_common_blocks("5581999999999", body_text = "Choose an option below") # With header and footer whapi_common_blocks( to = "5581999999999", body_text = "Do you confirm?", header_text = "Booking Confirmation", footer_text = "Reply now" ) ## End(Not run)
Calculates the difference in days between two dates (end - start).
Returns NA if the start date is missing.
whapi_date_diff_days(start, end)whapi_date_diff_days(start, end)
start |
Start date ( |
end |
End date ( |
Integer number of days, or NA if start is missing.
whapi_date_diff_days(Sys.Date() - 10, Sys.Date()) #> 10whapi_date_diff_days(Sys.Date() - 10, Sys.Date()) #> 10
Helper function to standardize parsing of Whapi message responses.
Whapi responses typically return a JSON object with a top-level element
sent and a nested message object containing details such as id,
status, timestamp, etc.
This function consolidates those fields into a flat tibble, making it easier
to work with message metadata in R.
whapi_extract_common_fields(out, fallback_to)whapi_extract_common_fields(out, fallback_to)
out |
A list (parsed JSON) as returned by |
fallback_to |
Character(1). A fallback chat id (usually the |
The function safely looks up fields in multiple possible locations, since Whapi responses are not always consistent across endpoints:
id: prefers out$message$id, then out$id, then out$message_id;
status: out$message$status or out$status;
timestamp: out$message$timestamp or out$timestamp;
chat_id: from out$message$chat_id, out$message$to, or fallback_to;
type: out$message$type or out$type;
sent: top-level out$sent (boolean, TRUE if successfully sent).
The timestamp is returned both raw (numeric, seconds since epoch) and as a
parsed POSIXct column (timestamp_dt, UTC).
A tibble with one row and the following columns:
id: message id;
to: recipient chat id (phone or group);
status: sending status (e.g., "pending", "sent");
timestamp: numeric epoch timestamp (seconds);
timestamp_dt: POSIXct parsed timestamp in UTC;
type: message type (e.g., "text", "image", "location");
sent: logical/boolean (TRUE if sent flag present);
resp: the full raw response list for inspection.
Used internally in wrappers like whapi_send_text(),
whapi_send_image(), whapi_send_document(),
whapi_send_location().
# Suppose `resp` is the parsed JSON returned from Whapi: out <- list( sent = TRUE, message = list( id = "abc123", chat_id = "[email protected]", timestamp = 1756426418, type = "location", status = "pending" ) ) whapi_extract_common_fields(out, fallback_to = "558199999999")# Suppose `resp` is the parsed JSON returned from Whapi: out <- list( sent = TRUE, message = list( id = "abc123", chat_id = "[email protected]", timestamp = 1756426418, type = "location", status = "pending" ) ) whapi_extract_common_fields(out, fallback_to = "558199999999")
Flatten Whapi webhook payload into tidy rows for persistence (robust)
whapi_flatten_webhook(payload, verbose = TRUE)whapi_flatten_webhook(payload, verbose = TRUE)
payload |
list parsed from JSON (e.g. plumber |
verbose |
logical, print logs with cli? |
tibble with normalized fields
Formats a date safely, returning a fallback value (na) when the input
is NULL or NA.
whapi_fmt_date(x, fmt = "%d/%m/%Y", na = "-")whapi_fmt_date(x, fmt = "%d/%m/%Y", na = "-")
x |
Date or coercible to |
fmt |
Date format passed to |
na |
Fallback string if the input is missing. |
A formatted date string, or the na placeholder if missing.
whapi_fmt_date(Sys.Date()) #> "31/08/2025" whapi_fmt_date(NA) #> "-"whapi_fmt_date(Sys.Date()) #> "31/08/2025" whapi_fmt_date(NA) #> "-"
Fetches profile information for one or more contacts using
GET /contacts/{ContactID}/profile and returns a tidy tibble.
This version assumes the response body contains:
name (string, user name),
about (string, user info in About section),
icon (string, profile preview icon URL),
icon_full (string, full avatar URL).
whapi_get_contact_profile( contacts, token = Sys.getenv("WHAPI_TOKEN", unset = ""), full = TRUE, timeout = 30, verbose = TRUE )whapi_get_contact_profile( contacts, token = Sys.getenv("WHAPI_TOKEN", unset = ""), full = TRUE, timeout = 30, verbose = TRUE )
contacts |
Character vector. Phones in E.164 digits (no "+") or chat IDs. |
token |
Bearer token. Defaults to env var |
full |
Logical. If |
timeout |
Numeric. Request timeout (seconds). Default |
verbose |
Logical. Print progress with cli? Default |
Each contacts element may be a phone number (free text) or a JID
(e.g., "[email protected]"). Phone numbers are normalized via
whapi_normalize_to() (12 digits total); JIDs are kept as-is.
When full = TRUE, _full=true is added to the querystring to request
higher-resolution avatars (if supported).
A tibble with one row per contact:
contact_id (input id or JID as queried),
name, about, icon, icon_full,
resp with the raw response (list).
## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") # Single: # whapi_get_contact_profile("5581999999999") # Mixed (number + group JID): # whapi_get_contact_profile(c("5581999999999", "[email protected]")) ## End(Not run)## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") # Single: # whapi_get_contact_profile("5581999999999") # Mixed (number + group JID): # whapi_get_contact_profile(c("5581999999999", "[email protected]")) ## End(Not run)
Get a WhatsApp message by ID (Whapi.Cloud)
whapi_get_message( message_id, resync = FALSE, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_get_message( message_id, resync = FALSE, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
message_id |
Character(1). The message ID to fetch. |
resync |
Logical(1). Whether to resync from the device. Default FALSE. |
token |
Bearer token (default from WHAPI_TOKEN env var). |
timeout |
Timeout in seconds. Default 30. |
verbose |
Print CLI progress? Default TRUE. |
Tibble with fields: id, type, subtype, chat_id, chat_name,
from, from_name, from_me, source, timestamp, timestamp_dt,
device_id, status, and raw resp.
# whapi_get_message("PsobWy36679._7w-wKmB9tMeGQ")# whapi_get_message("PsobWy36679._7w-wKmB9tMeGQ")
Prints a structured summary of a req object (Plumber request) to the console,
with colored and formatted output using the cli package.
This helper is useful for debugging APIs, inspecting request metadata,
and logging incoming payloads in a readable way.
The function logs:
HTTP method, path, query string, host, client IP/port, content type, and length.
Parsed arguments: argsQuery, argsBody, args.
Headers (with sensitive values redacted).
Cookies (always redacted).
Parsed request body (req$body or req$postBody).
whapi_log_plumber_req( req, show_headers = TRUE, show_cookies = TRUE, show_body = TRUE, max_chars = 2000L )whapi_log_plumber_req( req, show_headers = TRUE, show_cookies = TRUE, show_body = TRUE, max_chars = 2000L )
req |
A Plumber request object, usually passed automatically inside an endpoint.
Must contain fields such as |
show_headers |
Logical. Whether to print request headers (default: |
show_cookies |
Logical. Whether to print cookies (default: |
show_body |
Logical. Whether to print the parsed body or raw |
max_chars |
Integer. Maximum number of characters to print for large JSON or raw bodies.
Defaults to |
Invisibly returns NULL. The function is called for its side-effect
of printing formatted logs to the console.
cli::cli_inform(), cli::cli_rule(), cli::cli_verbatim()
## Not run: # Inside a Plumber endpoint #* @post /myendpoint function(req, res) { whapi_log_plumber_req(req) # Prints nicely formatted info about the incoming request list(success = TRUE) } # Print only metadata, no headers/body whapi_log_plumber_req(req, show_headers = FALSE, show_body = FALSE) ## End(Not run)## Not run: # Inside a Plumber endpoint #* @post /myendpoint function(req, res) { whapi_log_plumber_req(req) # Prints nicely formatted info about the incoming request list(success = TRUE) } # Print only metadata, no headers/body whapi_log_plumber_req(req, show_headers = FALSE, show_body = FALSE) ## End(Not run)
Converts R objects to a JSON string for easier inspection in logs.
Falls back to utils::str() output if jsonlite is not available
or if JSON conversion fails. Long outputs are truncated with whapi_trunc.
whapi_log_pretty_json(x, max = 2000L)whapi_log_pretty_json(x, max = 2000L)
x |
An R object (list, data frame, etc.). |
max |
Integer. Maximum number of characters to print (default: 2000). |
A character string containing JSON (pretty-printed if possible).
whapi_log_pretty_json(list(a = 1, b = "test")) whapi_log_pretty_json(mtcars[1:2, ], max = 100)whapi_log_pretty_json(list(a = 1, b = "test")) whapi_log_pretty_json(mtcars[1:2, ], max = 100)
Ensures that a character vector of identifiers is unique by appending
numeric suffixes (_2, _3, ...) when duplicates are found, while
preserving the original order.
whapi_make_unique(x)whapi_make_unique(x)
x |
A character vector of IDs (possibly with duplicates). |
This helper is particularly useful when generating button IDs for
WhatsApp interactive messages via Whapi. Even after whapi_slugifying labels,
duplicates may remain (e.g., two buttons both titled "Yes").
The function guarantees uniqueness by incrementally appending a suffix.
Algorithm:
Iterates through x in order;
Keeps a counter of how many times each ID has appeared;
First occurrence is left unchanged;
Subsequent duplicates get suffixed with _<n>.
A character vector of the same length with unique IDs.
whapi_slugify() for slug-safe ID creation.
whapi_make_unique(c("yes", "no", "yes", "yes", "maybe", "no")) # -> "yes", "no", "yes_2", "yes_3", "maybe", "no_2" # Combined with whapi_slugify titles <- c("Yes!", "Yes!", "No?") ids <- whapi_make_unique(whapi_slugify(titles)) tibble::tibble(title = titles, id = ids)whapi_make_unique(c("yes", "no", "yes", "yes", "maybe", "no")) # -> "yes", "no", "yes_2", "yes_3", "maybe", "no_2" # Combined with whapi_slugify titles <- c("Yes!", "Yes!", "No?") ids <- whapi_make_unique(whapi_slugify(titles)) tibble::tibble(title = titles, id = ids)
Marks a message as read using PUT /messages/{MessageID}.
This endpoint returns only a minimalistic ACK in the body:
{"success": true | false}.
whapi_mark_message_read( message_id, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_mark_message_read( message_id, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
message_id |
Character(1). Message ID (WAMID) to be marked as read. |
token |
Character(1). Bearer token. Default: |
timeout |
Numeric(1). Timeout (s). Default: 30. |
verbose |
Logical(1). Show logs via cli? Default: |
A tibble with the following columns:
id - the provided message_id;
status - "read" when success=TRUE, "error" otherwise;
success - logical value returned by the endpoint;
resp - raw response (list).
## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") # whapi_mark_message_read("PsqXn5SAD5v7HRA-wHqB9tMeGQ") ## End(Not run)## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") # whapi_mark_message_read("PsqXn5SAD5v7HRA-wHqB9tMeGQ") ## End(Not run)
Matches a digit-only needle inside a digit-only haystack.
If needle has at least min_partial digits, partial matching is allowed.
Otherwise, only exact matches are considered.
whapi_match_digits(haystack, needle, min_partial = 6)whapi_match_digits(haystack, needle, min_partial = 6)
haystack |
String with potential digits to search in. |
needle |
String with digits to search for. |
min_partial |
Minimum number of digits required to allow partial match. |
Logical (TRUE/FALSE) indicating whether a match was found.
whapi_match_digits("tel: 81998765432", "987654") #> TRUE whapi_match_digits("12345", "123", min_partial = 4) #> FALSEwhapi_match_digits("tel: 81998765432", "987654") #> TRUE whapi_match_digits("12345", "123", min_partial = 4) #> FALSE
Cleans and normalizes WhatsApp phone numbers by:
Skipping normalization if the entry already contains a JID suffix (e.g., "@s.whatsapp.net" or "@g.us");
Otherwise, removing all non-digit characters (+, spaces, parentheses, hyphens, etc.);
Removing the ninth digit (when present) right after the country code (CC) and area code (SS), in order to standardize to 12 or 13 digits.
whapi_normalize_to(to)whapi_normalize_to(to)
to |
Character. A character vector of WhatsApp numbers in free text format or JIDs. |
This function is primarily designed for Brazilian E.164 numbers (e.g.,
+55 (81) 9XXXX-YYYY).
A character vector of normalized IDs (phones -> digits only, JIDs kept as-is).
Removes all non-digit characters from the input string. Useful for cleaning phone numbers, CNPJs/CPFs, or process codes.
whapi_only_digits(x)whapi_only_digits(x)
x |
Character vector or string. If |
A string containing only numeric digits.
whapi_only_digits("(81) 98765-4321") #> "81987654321"whapi_only_digits("(81) 98765-4321") #> "81987654321"
Attempts to parse the body of a Plumber req object.
Supports both req$body (already parsed) and req$bodyRaw (raw binary).
If the body looks like JSON ({...} or [...]), it tries to parse it
with jsonlite::fromJSON. Otherwise, it returns the raw text.
whapi_parse_body(req)whapi_parse_body(req)
req |
A Plumber request object (list-like). |
A list representing the parsed body, or NULL if no body is found.
# Inside a Plumber endpoint: # parsed <- whapi_parse_body(req) # if (!is.null(parsed)) str(parsed)# Inside a Plumber endpoint: # parsed <- whapi_parse_body(req) # if (!is.null(parsed)) str(parsed)
Splits an incoming text (usually from a WhatsApp message) into a command
(the first token, starting with /) and its associated arguments.
Supports collapsing arguments into a single string or preserving them as
a character vector.
whapi_parse_command(text, collapse_args = TRUE)whapi_parse_command(text, collapse_args = TRUE)
text |
A character string containing the full message (e.g. |
collapse_args |
Logical, default |
A list with two elements:
commandThe command string (first token), trimmed (e.g. "/groe").
argumentsThe arguments, either collapsed as a single string
(default) or as a character vector, depending on collapse_args.
stringr::str_extract(), stringr::str_split()
whapi_parse_command("/groe ajuda") #> $command #> [1] "/groe" #> #> $arguments #> [1] "ajuda" whapi_parse_command("/groe nome empresa", collapse_args = FALSE) #> $command #> [1] "/groe" #> #> $arguments #> [1] "nome" "empresa"whapi_parse_command("/groe ajuda") #> $command #> [1] "/groe" #> #> $arguments #> [1] "ajuda" whapi_parse_command("/groe nome empresa", collapse_args = FALSE) #> $command #> [1] "/groe" #> #> $arguments #> [1] "nome" "empresa"
Generic helper wrapping httr2 to call Whapi endpoints.
Supports methods: "GET", "POST", "PUT".
Handles JSON encoding, retries, errors, and CLI logging.
whapi_perform_request( endpoint, payload = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE, method = c("POST", "GET", "PUT") )whapi_perform_request( endpoint, payload = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE, method = c("POST", "GET", "PUT") )
endpoint |
Character(1). Endpoint path (e.g. |
payload |
List. Request body (for POST/PUT) or query (for GET). Default: |
token |
Character(1). Bearer token. Defaults to env var |
timeout |
Numeric. Timeout in seconds. Default |
verbose |
Logical. Print progress via cli? Default |
method |
Character(1). HTTP method ( |
Parsed JSON response as a list.
# out <- whapi_perform_request("messages/text", list(to="5581...", body="Hi"), method="POST") # out <- whapi_perform_request("messages/12345", list(status="read"), method="PUT")# out <- whapi_perform_request("messages/text", list(to="5581...", body="Hi"), method="POST") # out <- whapi_perform_request("messages/12345", list(status="read"), method="PUT")
Sends (or removes) an emoji reaction to a message via
PUT /messages/{MessageID}/reaction.
The endpoint returns only { "success": true | false }.
whapi_react_to_message( message_id, emoji, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_react_to_message( message_id, emoji, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
message_id |
Character(1). Target message ID (WAMID). |
emoji |
Character(1). Emoji to react with.
To remove a reaction, pass an empty string |
token |
Character(1). Bearer token. Defaults to env var |
timeout |
Numeric(1). Request timeout in seconds. Default: 30. |
verbose |
Logical(1). Print CLI logs? Default: TRUE. |
A tibble with columns:
id - message_id reacted to
emoji - emoji used (or "" if removed)
status - "ok" if success, "error" otherwise
success - logical flag from API
resp - raw response (list)
Replaces sensitive header or cookie values (e.g., Authorization,
Cookie, X-API-Key) with the literal string "<redacted>".
Useful when logging HTTP requests while avoiding credential leaks.
whapi_redact(name, value)whapi_redact(name, value)
name |
Header or cookie name (character). |
value |
Header or cookie value (character). |
The original value, or "<redacted>" if the header is considered sensitive.
whapi_redact("Authorization", "Bearer abc123") whapi_redact("Content-Type", "application/json")whapi_redact("Authorization", "Bearer abc123") whapi_redact("Content-Type", "application/json")
Sends a document using Whapi's POST /messages/document.
Supports three input modes via type:
"file" : local path -> reads bytes and builds a data URI
(data:<mime>;name=<file>;base64,<...>);
"url" : direct http(s) URL;
"base64" : pre-built data URI (data:application/...;name=...;base64,...).
whapi_send_document( to, document, type = c("file", "url", "base64"), caption = NULL, filename = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_send_document( to, document, type = c("file", "url", "base64"), caption = NULL, filename = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
to |
Character(1). WhatsApp target (E.164 digits only, no "+") or chat id. |
document |
Character(1). File path (when |
type |
One of |
caption |
Optional caption text. |
filename |
Optional filename shown to the user. If omitted:
|
token |
Bearer token (defaults to env var |
timeout |
Numeric. Request timeout in seconds. Default |
verbose |
Logical. Print CLI messages? Default |
A tibble with id, to, status, timestamp, timestamp_dt, and raw resp.
## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_document("5581999999999", "report.pdf", type="file", caption="Monthly report") whapi_send_document("5581999999999", "https://example.com/contract.docx", type="url") b <- openssl::base64_encode(readBin("memo.odt","raw",file.info("memo.odt")$size)) du <- sprintf("data:application/vnd.oasis.opendocument.text;name=%s;base64,%s", basename("memo.odt"), b) whapi_send_document("5581999999999", du, type="base64") ## End(Not run)## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_document("5581999999999", "report.pdf", type="file", caption="Monthly report") whapi_send_document("5581999999999", "https://example.com/contract.docx", type="url") b <- openssl::base64_encode(readBin("memo.odt","raw",file.info("memo.odt")$size)) du <- sprintf("data:application/vnd.oasis.opendocument.text;name=%s;base64,%s", basename("memo.odt"), b) whapi_send_document("5581999999999", du, type="base64") ## End(Not run)
Sends an image using Whapi's POST /messages/image.
Supports three input modes through type:
"file": local path -> reads bytes and builds a data:<mime>;name=<file>;base64,<...> URI
"url": direct http(s) URL
"base64": pre-built data URI (data:image/...;base64,...)
whapi_send_image( to, image, type = c("file", "url", "base64"), caption = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_send_image( to, image, type = c("file", "url", "base64"), caption = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
to |
Character(1). WhatsApp target (E.164 digits only, no "+") or chat id. |
image |
Character(1). File path (for |
type |
One of |
caption |
Optional caption text. |
token |
Bearer token (defaults to env var |
timeout |
Numeric. Timeout in seconds. Default |
verbose |
Logical. Show CLI messages? Default |
A tibble with id, to, status, timestamp, timestamp_dt, and raw resp.
## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_image("5581999999999", image = "card.png", type = "file", caption = "Card") whapi_send_image("5581999999999", image = "https://site.com/img.png", type = "url") b64 <- openssl::base64_encode(readBin("card.png","raw",file.info("card.png")$size)) data_uri <- sprintf("data:image/png;name=%s;base64,%s", basename("card.png"), b64) whapi_send_image("5581999999999", image = data_uri, type = "base64") ## End(Not run)## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_image("5581999999999", image = "card.png", type = "file", caption = "Card") whapi_send_image("5581999999999", image = "https://site.com/img.png", type = "url") b64 <- openssl::base64_encode(readBin("card.png","raw",file.info("card.png")$size)) data_uri <- sprintf("data:image/png;name=%s;base64,%s", basename("card.png"), b64) whapi_send_image("5581999999999", image = data_uri, type = "base64") ## End(Not run)
Sends an interactive LIST message via Whapi.
Sections/rows are validated by whapi_validate_list_sections(). The payload
reuses whapi_common_blocks() to keep structure consistent across interactive
message types.
whapi_send_list( to, body_text, list_sections, list_label = "Choose...", header_text = NULL, footer_text = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_send_list( to, body_text, list_sections, list_label = "Choose...", header_text = NULL, footer_text = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
to |
Character(1). Phone in E.164 digits (without "+") or group id. |
body_text |
Character(1). Main body text. |
list_sections |
A list of sections. Each section is a named list:
|
list_label |
Character(1), optional. Button label that opens the list.
Default: |
header_text, footer_text
|
Character(1), optional. Header/footer texts. |
token |
Bearer token. Defaults to env var |
timeout |
Numeric. Request timeout in seconds. Default 30. |
verbose |
Logical. Print CLI messages? Default TRUE. |
A tibble with id, to, status, timestamp, and the raw response in resp.
whapi_validate_list_sections(), whapi_common_blocks(),
whapi_perform_request(), whapi_extract_common_fields()
## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") sections <- list( list( title = "Burgers", rows = list( list(id="b1", title="Plain", description="No cheese, no sauce"), list(id="b2", title="Cheese", description="With melted cheese") ) ), list( title = "Drinks", rows = list( list(id="d1", title="Water"), list(id="d2", title="Soda", description="Assorted flavors") ) ) ) whapi_send_list( to = "5581999999999", body_text = "Choose your order:", list_sections = sections, list_label = "Open menu", header_text = "Our Menu", footer_text = "Thanks!" ) ## End(Not run)## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") sections <- list( list( title = "Burgers", rows = list( list(id="b1", title="Plain", description="No cheese, no sauce"), list(id="b2", title="Cheese", description="With melted cheese") ) ), list( title = "Drinks", rows = list( list(id="d1", title="Water"), list(id="d2", title="Soda", description="Assorted flavors") ) ) ) whapi_send_list( to = "5581999999999", body_text = "Choose your order:", list_sections = sections, list_label = "Open menu", header_text = "Our Menu", footer_text = "Thanks!" ) ## End(Not run)
Sends a location message using Whapi's POST /messages/location.
Supports optional name (short title) and address (human-readable).
whapi_send_location( to, latitude, longitude, name = NULL, address = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_send_location( to, latitude, longitude, name = NULL, address = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
to |
Character(1). WhatsApp target (E.164 digits only, no "+") or chat id. |
latitude |
Numeric(1). Latitude in decimal degrees (range: -90..90). |
longitude |
Numeric(1). Longitude in decimal degrees (range: -180..180). |
name |
Optional character(1). Short title for the location. |
address |
Optional character(1). Human-readable address/description. |
token |
Bearer token (defaults to env var |
timeout |
Numeric. Request timeout (seconds). Default 30. |
verbose |
Logical. Print CLI messages? Default TRUE. |
A tibble with id, to, status, timestamp, timestamp_dt, and raw resp.
## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_location("5581999999999", latitude = -8.063169, longitude = -34.871139, name = "Marco Zero", address = "Recife, PE") # Group id example whapi_send_location("[email protected]", -8.045, -34.91) ## End(Not run)## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_location("5581999999999", latitude = -8.063169, longitude = -34.871139, name = "Marco Zero", address = "Recife, PE") # Group id example whapi_send_location("[email protected]", -8.045, -34.91) ## End(Not run)
Sends an interactive buttons message that mixes url, call, and/or
copy actions. Input buttons are normalized/validated by
whapi_coerce_buttons_mixed() (aliases mapped to title, auto id creation,
required fields per type).
whapi_send_mixed_actions( to, body_text, buttons, header_text = NULL, footer_text = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_send_mixed_actions( to, body_text, buttons, header_text = NULL, footer_text = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
to |
Character(1). Phone in E.164 digits (without "+") or group id. |
body_text |
Character(1). Main body text. |
buttons |
Data frame or list. Up to 3 items; each must define a
|
header_text, footer_text
|
Character (optional). Header/footer texts. |
token |
Bearer token. Defaults to env var |
timeout |
Numeric. Request timeout (seconds). Default 30. |
verbose |
Logical. Print CLI messages? Default TRUE. |
A tibble with fields id, to, status, timestamp, and the raw response
in resp.
whapi_coerce_buttons_mixed(), whapi_common_blocks(), whapi_perform_request()
## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_mixed_actions( to = "5581999999999", body_text = "Pick an option:", buttons = list( list(type="url", title="Website", url="https://example.com"), list(type="call", title="Call us", phone_number="5581999999999"), list(type="copy", title="Copy OTP", copy_code="123456") ) ) ## End(Not run)## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") whapi_send_mixed_actions( to = "5581999999999", body_text = "Pick an option:", buttons = list( list(type="url", title="Website", url="https://example.com"), list(type="call", title="Call us", phone_number="5581999999999"), list(type="copy", title="Copy OTP", copy_code="123456") ) ) ## End(Not run)
Sends an interactive message of type QUICK REPLY via Whapi.
Each button is normalized/validated by whapi_coerce_buttons_quick() and
automatically gets a unique slugified id if missing.
whapi_send_quick_reply( to, body_text, buttons, header_text = NULL, footer_text = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_send_quick_reply( to, body_text, buttons, header_text = NULL, footer_text = NULL, token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
to |
Character (length 1). Phone in E.164 digits (without "+") or group id. |
body_text |
Character. Main body text. |
buttons |
Data frame or list. Up to 3 items; fields: |
header_text, footer_text
|
Character (optional). Header/footer texts. |
token |
Bearer token. Defaults to env var WHAPI_TOKEN. |
timeout |
Numeric, request timeout in seconds. Default 30. |
verbose |
Logical, print CLI messages. Default TRUE. |
A tibble with fields id, to, status, timestamp, and raw response resp.
whapi_coerce_buttons_quick(), whapi_common_blocks(), whapi_perform_request()
## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") # whapi_send_quick_reply( # to = "5581999999999", # body_text = "Do you confirm?", # buttons = tibble::tribble(~title, "YES", "NO") # ) ## End(Not run)## Not run: # Sys.setenv(WHAPI_TOKEN = "your_token_here") # whapi_send_quick_reply( # to = "5581999999999", # body_text = "Do you confirm?", # buttons = tibble::tribble(~title, "YES", "NO") # ) ## End(Not run)
Sends a WhatsApp sticker via POST /messages/sticker.
The sticker must be in WebP format (image/webp).
whapi_send_sticker( to, sticker, type = c("file", "url", "base64"), token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )whapi_send_sticker( to, sticker, type = c("file", "url", "base64"), token = Sys.getenv("WHAPI_TOKEN", unset = ""), timeout = 30, verbose = TRUE )
to |
Character(1). WhatsApp target (E.164 digits only, no "+") or chat id. |
sticker |
Character(1).
|
type |
One of c("file","url","base64"). Default = "file". |
token |
Bearer token (env var WHAPI_TOKEN if not provided). |
timeout |
Numeric. Request timeout (seconds). Default 30. |
verbose |
Logical. Print CLI messages? Default TRUE. |
tibble with id, to, status, timestamp, and raw response in resp.
## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") # 1) Local file whapi_send_sticker("558191812121", sticker = "sticker.webp", type = "file") # 2) Remote URL whapi_send_sticker("558191812121", sticker = "https://example.com/condepe.webp", type = "url") # 3) Pre-encoded Base64 b64 <- openssl::base64_encode(readBin("sticker.webp","raw",file.info("sticker.webp")$size)) data_uri <- sprintf("data:image/webp;name=%s;base64,%s", basename("sticker.webp"), b64) whapi_send_sticker("558191812121", sticker = data_uri, type = "base64") ## End(Not run)## Not run: Sys.setenv(WHAPI_TOKEN = "your_token_here") # 1) Local file whapi_send_sticker("558191812121", sticker = "sticker.webp", type = "file") # 2) Remote URL whapi_send_sticker("558191812121", sticker = "https://example.com/condepe.webp", type = "url") # 3) Pre-encoded Base64 b64 <- openssl::base64_encode(readBin("sticker.webp","raw",file.info("sticker.webp")$size)) data_uri <- sprintf("data:image/webp;name=%s;base64,%s", basename("sticker.webp"), b64) whapi_send_sticker("558191812121", sticker = data_uri, type = "base64") ## End(Not run)
Send a text message via Whapi.Cloud
whapi_send_text( to, body, token = Sys.getenv("WHAPI_TOKEN", unset = ""), quoted = NULL, edit = NULL, typing_time = NULL, no_link_preview = NULL, wide_link_preview = NULL, mentions = NULL, view_once = NULL, timeout = 30, verbose = TRUE )whapi_send_text( to, body, token = Sys.getenv("WHAPI_TOKEN", unset = ""), quoted = NULL, edit = NULL, typing_time = NULL, no_link_preview = NULL, wide_link_preview = NULL, mentions = NULL, view_once = NULL, timeout = 30, verbose = TRUE )
to |
Character. WhatsApp number in full international format WITHOUT "+" (digits only, e.g. "5581999999999"), or an existing group/channel id. |
body |
Character. Text of the message (UTF-8). |
token |
Character. Whapi Bearer token. By default, taken from the environment variable WHAPI_TOKEN if not provided. |
quoted, edit
|
Character (optional). Message IDs to quote or edit. |
typing_time |
Numeric (optional). Seconds to simulate typing. |
no_link_preview |
Logical (optional). TRUE to disable link preview. |
wide_link_preview |
Logical (optional). TRUE to enable wide preview for links. |
mentions |
Character vector (optional). Numbers to mention (without "+").
Remember to include @ |
view_once |
Logical (optional). TRUE to mark message as "view once". |
timeout |
Numeric. Request timeout in seconds. Default: 30. |
verbose |
Logical. Print messages via cli? Default: TRUE. |
A tibble with essential information (id, to, status, timestamp) and
the full API response in column resp (as list).
## Not run: # Make sure you set WHAPI_TOKEN in your environment or pass via argument Sys.setenv(WHAPI_TOKEN = "your_token_here") # Simple example: whapi_send_text("5581999999999", "Hello! Test message via API") # With extra options: whapi_send_text( to = "5581999999999", body = "Hello, @5581999999999 Ola", mentions = c("5581999999999"), typing_time = 2, no_link_preview = TRUE ) ## End(Not run)## Not run: # Make sure you set WHAPI_TOKEN in your environment or pass via argument Sys.setenv(WHAPI_TOKEN = "your_token_here") # Simple example: whapi_send_text("5581999999999", "Hello! Test message via API") # With extra options: whapi_send_text( to = "5581999999999", body = "Hello, @5581999999999 Ola", mentions = c("5581999999999"), typing_time = 2, no_link_preview = TRUE ) ## End(Not run)
Converts free-text labels into a safe "slug" format suitable for use as
message button IDs or other identifiers in Whapi API requests.
Ensures that IDs contain only lowercase letters, digits, and underscores,
and are never empty (defaults to "btn" if the input is blank).
whapi_slugify(x)whapi_slugify(x)
x |
A character vector of labels. |
This function is particularly useful when creating interactive messages
(buttons, lists) in WhatsApp via Whapi, where each button requires a valid
id. By whapi_slugifying titles automatically, we can safely generate IDs even if
users provide arbitrary labels with spaces, accents, or symbols.
Transformation steps:
Convert to lowercase;
Replace any sequence of non-alphanumeric characters with _;
Trim leading/trailing underscores;
Replace empty results with "btn".
A character vector of the same length with slugified IDs.
Used internally in whapi_send_quick_reply() and other
interactive message helpers.
whapi_slugify(c("Yes!", "Call Us", "Sale!", "###")) # -> "yes", "call_us", "promocao_rapida", "btn" # Use case in button creation: titles <- c("Buy Now", "Learn More") ids <- whapi_slugify(titles) tibble::tibble(title = titles, id = ids)whapi_slugify(c("Yes!", "Call Us", "Sale!", "###")) # -> "yes", "call_us", "promocao_rapida", "btn" # Use case in button creation: titles <- c("Buy Now", "Learn More") ids <- whapi_slugify(titles) tibble::tibble(title = titles, id = ids)
Converts input text to lowercase and strips diacritics (accents) by
applying a latin-ascii transliteration. Useful for normalization before
matching or slug generation.
whapi_to_ascii_lower(x)whapi_to_ascii_lower(x)
x |
Character vector or string. If |
A character vector in lowercase ASCII without accents.
whapi_to_ascii_lower("Sao Paulo") #> "sao paulo"whapi_to_ascii_lower("Sao Paulo") #> "sao paulo"
Utility function to convert a Unix timestamp (seconds since 1970-01-01 UTC)
into a POSIXct object.
Uses lubridate::as_datetime() for readability and consistency with the
tidyverse ecosystem.
whapi_to_posixct(x)whapi_to_posixct(x)
x |
A numeric or character vector representing a Unix timestamp
(seconds since epoch). Can be |
A POSIXct object (in UTC) or NA if x is NULL or NA.
# Single timestamp whapi_to_posixct(1756426418) # Vector of timestamps (with NA) whapi_to_posixct(c(1756426418, NA)) # Character input whapi_to_posixct("1756426418")# Single timestamp whapi_to_posixct(1756426418) # Vector of timestamps (with NA) whapi_to_posixct(c(1756426418, NA)) # Character input whapi_to_posixct("1756426418")
Helper function to shorten long strings when printing to logs.
If the input string exceeds max characters, it is truncated and
suffixed with "... (truncated)".
whapi_trunc(x, max = 2000L)whapi_trunc(x, max = 2000L)
x |
A character string (or coercible to character). |
max |
Integer. Maximum number of characters to display (default: 2000). |
A character string, possibly truncated.
whapi_trunc("short text", max = 10) whapi_trunc(paste(rep("a", 5000), collapse = ""), max = 20)whapi_trunc("short text", max = 10) whapi_trunc(paste(rep("a", 5000), collapse = ""), max = 20)
list_sections for Whapi LIST interactive messagesInternal helper that validates the structure of list_sections used in
Whapi LIST messages. Each section must provide a non-empty title
and a non-empty rows list. Each row must provide non-empty id and
title. The description field is optional.
whapi_validate_list_sections(list_sections, verbose = TRUE, trim = TRUE)whapi_validate_list_sections(list_sections, verbose = TRUE, trim = TRUE)
list_sections |
A list of sections; each section is a named list with
|
verbose |
Logical (default |
trim |
Logical (default |
Expected structure:
list_sections <- list(
list(
title = "Section title",
rows = list(
list(id = "r1", title = "Row title", description = "Optional"),
...
)
),
...
)
This function performs lightweight validation and (optionally) trims whitespace from titles and ids to avoid subtle formatting issues.
The (possibly trimmed) list_sections object, invisibly unchanged in shape.
Helpers for interactive payloads such as whapi_coerce_buttons_base(),
whapi_coerce_buttons_quick(), and whapi_coerce_buttons_mixed().
sections <- list( list( title = "Burgers", rows = list( list(id = "r1", title = "Plain", description = "No cheese, no sauce"), list(id = "r2", title = "Cheese", description = "With melted cheese") ) ) ) whapi_validate_list_sections(sections)sections <- list( list( title = "Burgers", rows = list( list(id = "r1", title = "Plain", description = "No cheese, no sauce"), list(id = "r2", title = "Cheese", description = "With melted cheese") ) ) ) whapi_validate_list_sections(sections)