Skip to content

Commit 2c6cc55

Browse files
feat: apply June 2026 Cloud API changelog entries (#411–#418) (#55)
Co-authored-by: Evan <clcl6084@gmail.com>
1 parent 1aa9445 commit 2c6cc55

15 files changed

Lines changed: 435 additions & 10 deletions

File tree

.changeset/olive-pugs-repeat.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'meta-cloud-api': minor
3+
---
4+
5+
Apply June 2026 Cloud API changelog entries (#411#418)
6+
7+
- **Username API** (`phoneNumbers`): new `getUsername`, `updateUsername`, `deleteUsername`, and `getUsernameSuggestions` methods with `transfer_action` (`'none' | 'force_transfer'`) support, plus the new `147005` error code (username transfer required) in `WHATSAPP_ERROR_CODES`.
8+
- **Calling API**: per-call `recording` and `transcription` configuration on `initiateCall` and `acceptCall`, and new `call_recording_available` / `call_transcription_available` webhook events with `call_recording` / `call_transcript` payload types.
9+
- **SIP call webhooks**: new `call_created` call event type; business-scoped user ID fields (`from_user_id`, `from_parent_user_id`) on call entries.
10+
- **Coexistence webhooks**: new `edit` and `revoke` message webhook types (`EditMessage`, `RevokeMessage`) with business-scoped user ID fields, and matching `MessageTypesEnum.Edit` / `MessageTypesEnum.Revoke` members.

CLOUD_API_CHANGELOG.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,38 @@
44
> Updated: 2026-06-09T06:43:10.860Z
55
66

7+
## July 3, 2026
8+
9+
- [x] **#418** Documented geographic availability for the In-App Signup API, including the regions where it is not available.
10+
11+
## June 30, 2026
12+
13+
- [x] **#417** Added call recording and transcription for the WhatsApp Business Calling API.
14+
15+
## June 29, 2026
16+
17+
- [x] **#416** Username reservation and adoption go live June 29, 2026; BSUID message-send support and the contact-request button move to July 2026.
18+
19+
## June 26, 2026
20+
21+
- [x] **#415** Added SIP call webhooks support with call_created and terminate events for SIP-enabled numbers.
22+
23+
## June 25, 2026
24+
25+
- [x] **#414** WhatsApp Cloud API Calling voicemail is now generally available.
26+
27+
## June 12, 2026
28+
29+
- [x] **#413** Documented business-side message edits and revokes in the coexistence edit and revoke messages webhooks for business-scoped user IDs.
30+
31+
## June 11, 2026
32+
33+
- [x] **#412** Corrected the Terms of Service URL required in the In-App Signup API policy.tos field. Removed the United Kingdom from the unsupported regions for template analytics and template group analytics.
34+
35+
## June 10, 2026
36+
37+
- [x] **#411** Documented the optional transfer_action request parameter and the new 147005 error code for the WhatsApp Business Username API.
38+
739
## June 4, 2026
840

941
- [x] **#410** Clarified that deleting a business phone number is not supported via the API; use WhatsApp Manager.
@@ -1035,4 +1067,4 @@
10351067

10361068
---
10371069

1038-
**Progress: 410/411 (100%)**
1070+
**Progress: 419/419 (100%)**

docs/calling.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Configure calling, check permissions, and control call sessions.
1313
- Call actions use `action` values: `connect`, `pre_accept`, `accept`, `reject`, `terminate`.
1414
- `session` must include `sdp_type` and `sdp`.
1515
- `biz_opaque_callback_data` is optional for tracking.
16+
- `initiateCall` and `acceptCall` accept optional per-call `recording` and `transcription` configs (`status`, `purpose`, `announcement_language`). Results arrive via the `call_recording_available` / `call_transcription_available` webhook events on the `calls` field.
17+
- SIP-enabled numbers receive `call_created` and `terminate` call webhooks; SIP webhooks omit the `session` object since signaling is handled via SIP.
1618

1719
## Example
1820
```ts

docs/phone-numbers.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ Retrieve phone number details, request verification codes, configure conversatio
1919
- POST /{PHONE_NUMBER_ID}/official_business_account
2020
- GET /{PHONE_NUMBER_ID}/business_compliance_info
2121
- POST /{PHONE_NUMBER_ID}/business_compliance_info
22+
- GET /{PHONE_NUMBER_ID}/username
23+
- POST /{PHONE_NUMBER_ID}/username
24+
- DELETE /{PHONE_NUMBER_ID}/username
25+
- GET /{PHONE_NUMBER_ID}/username_suggestions
2226

2327
## Notes
2428
- Fields can be a comma-separated string or string array.
29+
- Usernames support an optional `transfer_action` (`none` or `force_transfer`). If the API returns error code `147005`, the username is in use by another number in your portfolio — retry with `force_transfer`.
30+
- Username `status` is `approved` (visible when the feature is available) or `reserved` (approved but not yet visible).
2531
- `messaging_limit_tier` is typed as known Meta tiers plus future string values, because Meta has changed tier names over time.
2632
- Official Business Account application payloads support the website fields Meta publishes in the OpenAPI examples and schema: `website_url` and `business_website_url`.
2733
- Verification `code_method` is `SMS` or `VOICE`.
@@ -55,6 +61,13 @@ await client.phoneNumbers.setConversationalAutomation({
5561
});
5662

5763
const throughput = await client.phoneNumbers.getThroughput();
64+
65+
const { username, status } = await client.phoneNumbers.getUsername();
66+
67+
await client.phoneNumbers.updateUsername({
68+
username: 'jaspersmarket',
69+
transfer_action: 'none',
70+
});
5871
```
5972

6073
## Example Details
@@ -63,3 +76,4 @@ const throughput = await client.phoneNumbers.getThroughput();
6376
- `requestVerificationCode` uses `code_method` and `language` to deliver the code.
6477
- `setConversationalAutomation` toggles `enable_welcome_message` and sets user-facing `prompts`.
6578
- `getThroughput` reads throughput info for the phone number.
79+
- `getUsername` / `updateUsername` / `deleteUsername` / `getUsernameSuggestions` manage the phone number's username.

src/api/calling/CallingApi.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export default class CallingApi extends BaseAPI implements calling.CallingClass
101101
* @param params.to - The recipient's WhatsApp phone number.
102102
* @param params.session - The call session configuration.
103103
* @param params.biz_opaque_callback_data - Optional opaque data passed back in webhooks.
104+
* @param params.recording - Optional per-call recording configuration.
105+
* @param params.transcription - Optional per-call transcription configuration.
104106
* @returns The initiated call response with call ID and status.
105107
* @see https://developers.facebook.com/documentation/business-messaging/whatsapp/calling/reference/
106108
*/
@@ -116,11 +118,15 @@ export default class CallingApi extends BaseAPI implements calling.CallingClass
116118
action: calling.CallAction;
117119
session: calling.CallSession;
118120
biz_opaque_callback_data?: string;
121+
recording?: calling.CallRecordingConfig;
122+
transcription?: calling.CallTranscriptionConfig;
119123
};
120124

121125
if (params.biz_opaque_callback_data) {
122126
body.biz_opaque_callback_data = params.biz_opaque_callback_data;
123127
}
128+
if (params.recording) body.recording = params.recording;
129+
if (params.transcription) body.transcription = params.transcription;
124130

125131
return this.sendJson(
126132
HttpMethodsEnum.Post,
@@ -168,6 +174,8 @@ export default class CallingApi extends BaseAPI implements calling.CallingClass
168174
* @param params.call_id - The ID of the call to accept.
169175
* @param params.session - Optional session configuration for the call.
170176
* @param params.biz_opaque_callback_data - Optional opaque data passed back in webhooks.
177+
* @param params.recording - Optional per-call recording configuration.
178+
* @param params.transcription - Optional per-call transcription configuration.
171179
* @returns The call action response confirming acceptance.
172180
* @see https://developers.facebook.com/documentation/business-messaging/whatsapp/calling/reference/
173181
*/
@@ -182,12 +190,16 @@ export default class CallingApi extends BaseAPI implements calling.CallingClass
182190
action: calling.CallAction;
183191
session?: calling.CallSession;
184192
biz_opaque_callback_data?: string;
193+
recording?: calling.CallRecordingConfig;
194+
transcription?: calling.CallTranscriptionConfig;
185195
};
186196

187197
if (params.session) body.session = params.session;
188198
if (params.biz_opaque_callback_data) {
189199
body.biz_opaque_callback_data = params.biz_opaque_callback_data;
190200
}
201+
if (params.recording) body.recording = params.recording;
202+
if (params.transcription) body.transcription = params.transcription;
191203

192204
return this.sendJson(
193205
HttpMethodsEnum.Post,

src/api/calling/__test__/unit.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,44 @@ describe('Calling API - Unit Tests', () => {
6161
session: { sdp_type: 'offer', sdp: 'v=0' },
6262
});
6363
});
64+
65+
it('initiates a call with recording and transcription', async () => {
66+
await whatsApp.calling.initiateCall({
67+
to: '14085551234',
68+
session: { sdp_type: 'offer', sdp: 'v=0' },
69+
recording: { status: 'ENABLED', purpose: 'Quality assurance', announcement_language: 'en_US' },
70+
transcription: { status: 'ENABLED', purpose: 'Quality assurance', announcement_language: 'en_US' },
71+
});
72+
73+
const [method, endpoint, _, body] = mockRequestSend.mock.calls[0];
74+
expect(method).toBe('POST');
75+
expect(endpoint).toBe(`${whatsApp.requester.phoneNumberId}/calls`);
76+
expect(JSON.parse(body)).toEqual({
77+
messaging_product: 'whatsapp',
78+
to: '14085551234',
79+
action: 'connect',
80+
session: { sdp_type: 'offer', sdp: 'v=0' },
81+
recording: { status: 'ENABLED', purpose: 'Quality assurance', announcement_language: 'en_US' },
82+
transcription: { status: 'ENABLED', purpose: 'Quality assurance', announcement_language: 'en_US' },
83+
});
84+
});
85+
86+
it('accepts a call with recording configuration', async () => {
87+
await whatsApp.calling.acceptCall({
88+
call_id: 'wacid.123',
89+
session: { sdp_type: 'answer', sdp: 'v=0' },
90+
recording: { status: 'ENABLED', purpose: 'Quality assurance', announcement_language: 'en' },
91+
});
92+
93+
const [method, endpoint, _, body] = mockRequestSend.mock.calls[0];
94+
expect(method).toBe('POST');
95+
expect(endpoint).toBe(`${whatsApp.requester.phoneNumberId}/calls`);
96+
expect(JSON.parse(body)).toEqual({
97+
messaging_product: 'whatsapp',
98+
call_id: 'wacid.123',
99+
action: 'accept',
100+
session: { sdp_type: 'answer', sdp: 'v=0' },
101+
recording: { status: 'ENABLED', purpose: 'Quality assurance', announcement_language: 'en' },
102+
});
103+
});
64104
});

src/api/calling/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import CallingApi from './CallingApi';
44

55
export default CallingApi;
66

7-
export { CallingApi };
8-
97
export type {
108
AcceptCallRequest,
119
CallAction,
1210
CallActionResponse,
11+
CallAnnouncementLanguage,
1312
CallbackPermissionStatus,
13+
CallCaptureStatus,
1414
CallHours,
1515
CallHoursDay,
1616
CallHoursStatus,
@@ -23,8 +23,10 @@ export type {
2323
CallPermissionAction,
2424
CallPermissionLimit,
2525
CallPermissionsResponse,
26+
CallRecordingConfig,
2627
CallSdpType,
2728
CallSession,
29+
CallTranscriptionConfig,
2830
HolidaySchedule,
2931
InitiateCallRequest,
3032
InitiateCallResponse,
@@ -37,3 +39,4 @@ export type {
3739
UpdateCallingSettingsRequest,
3840
WeeklyOperatingHours,
3941
} from './types';
42+
export { CallingApi };

src/api/calling/types/calling.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,65 @@ export type CallSession = {
9696

9797
export type CallAction = 'connect' | 'pre_accept' | 'accept' | 'reject' | 'terminate';
9898

99+
export type CallCaptureStatus = 'ENABLED' | 'DISABLED';
100+
101+
/**
102+
* Supported announcement locales for call recording / transcription.
103+
* The announcement is played to both participants before capture starts.
104+
*/
105+
export type CallAnnouncementLanguage =
106+
| 'en'
107+
| 'en_US'
108+
| 'en_AU'
109+
| 'en_CA'
110+
| 'en_GB'
111+
| 'en_IN'
112+
| 'en_NZ'
113+
| 'nl'
114+
| 'fr'
115+
| 'de'
116+
| 'hi'
117+
| 'it'
118+
| 'kn'
119+
| 'pt'
120+
| 'es'
121+
| 'es_ES'
122+
| 'te'
123+
| 'vi'
124+
| (string & {});
125+
126+
/**
127+
* Per-call recording configuration. When status is ENABLED, `purpose` and
128+
* `announcement_language` are required; the recording arrives via the
129+
* `call_recording_available` webhook event.
130+
*/
131+
export type CallRecordingConfig = {
132+
status: CallCaptureStatus;
133+
/** Reason for recording, max 250 characters — required when status is ENABLED */
134+
purpose?: string;
135+
/** Locale of the pre-recording announcement — required when status is ENABLED */
136+
announcement_language?: CallAnnouncementLanguage;
137+
};
138+
139+
/**
140+
* Per-call transcription configuration. When status is ENABLED, `purpose` and
141+
* `announcement_language` are required; the transcript arrives via the
142+
* `call_transcription_available` webhook event.
143+
*/
144+
export type CallTranscriptionConfig = {
145+
status: CallCaptureStatus;
146+
/** Reason for transcription, max 250 characters — required when status is ENABLED */
147+
purpose?: string;
148+
/** Locale of the pre-transcription announcement — required when status is ENABLED */
149+
announcement_language?: CallAnnouncementLanguage;
150+
};
151+
99152
export type InitiateCallRequest = {
100153
to: string;
101154
session: CallSession;
102155
biz_opaque_callback_data?: string;
156+
recording?: CallRecordingConfig;
157+
transcription?: CallTranscriptionConfig;
103158
};
104159

105160
export type PreAcceptCallRequest = {
@@ -111,6 +166,8 @@ export type AcceptCallRequest = {
111166
call_id: string;
112167
session?: CallSession;
113168
biz_opaque_callback_data?: string;
169+
recording?: CallRecordingConfig;
170+
transcription?: CallTranscriptionConfig;
114171
};
115172

116173
export type RejectCallRequest = {

src/api/phone/PhoneNumberApi.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
// - POST /{PHONE_NUMBER_ID}/official_business_account
1717
// - GET /{PHONE_NUMBER_ID}/business_compliance_info
1818
// - POST /{PHONE_NUMBER_ID}/business_compliance_info
19+
// - GET /{PHONE_NUMBER_ID}/username
20+
// - POST /{PHONE_NUMBER_ID}/username
21+
// - DELETE /{PHONE_NUMBER_ID}/username
22+
// - GET /{PHONE_NUMBER_ID}/username_suggestions
1923

2024
import { BaseAPI } from '../../types/base';
2125
import { HttpMethodsEnum, WabaConfigEnum } from '../../types/enums';
@@ -389,4 +393,80 @@ export default class PhoneNumberApi extends BaseAPI implements phoneNumber.Phone
389393
JSON.stringify(params),
390394
);
391395
}
396+
397+
/**
398+
* Get the username currently associated with the business phone number.
399+
*
400+
* @returns A promise that resolves with the username and its status
401+
* (`'approved'` or `'reserved'`).
402+
*
403+
* @see {@link https://developers.facebook.com/documentation/business-messaging/whatsapp/business-phone-numbers/usernames/ | Usernames}
404+
*/
405+
async getUsername(): Promise<phoneNumber.UsernameResponse> {
406+
return this.sendJson(
407+
HttpMethodsEnum.Get,
408+
`${this.config[WabaConfigEnum.PhoneNumberId]}/username`,
409+
this.config[WabaConfigEnum.RequestTimeout],
410+
null,
411+
);
412+
}
413+
414+
/**
415+
* Adopt or change the username for the business phone number.
416+
*
417+
* If the desired username is already in use by another phone number in your
418+
* portfolio, the API responds with error code `147005`; retry with
419+
* `transfer_action: 'force_transfer'` to move the username to this number.
420+
*
421+
* @param params - The username request.
422+
* @param params.username - The desired username.
423+
* @param params.transfer_action - Optional. `'none'` (default) or `'force_transfer'`.
424+
* @returns A promise that resolves with a success indicator.
425+
*
426+
* @see {@link https://developers.facebook.com/documentation/business-messaging/whatsapp/business-phone-numbers/usernames/ | Usernames}
427+
*
428+
* @example
429+
* ```ts
430+
* await whatsappClient.phoneNumbers.updateUsername({
431+
* username: 'jaspersmarket',
432+
* transfer_action: 'force_transfer',
433+
* });
434+
* ```
435+
*/
436+
async updateUsername(params: phoneNumber.UpdateUsernameRequest): Promise<ResponseSuccess> {
437+
return this.sendJson(
438+
HttpMethodsEnum.Post,
439+
`${this.config[WabaConfigEnum.PhoneNumberId]}/username`,
440+
this.config[WabaConfigEnum.RequestTimeout],
441+
JSON.stringify(params),
442+
);
443+
}
444+
445+
/**
446+
* Delete the username associated with the business phone number.
447+
*
448+
* @see {@link https://developers.facebook.com/documentation/business-messaging/whatsapp/business-phone-numbers/usernames/ | Usernames}
449+
*/
450+
async deleteUsername(): Promise<ResponseSuccess> {
451+
return this.sendJson(
452+
HttpMethodsEnum.Delete,
453+
`${this.config[WabaConfigEnum.PhoneNumberId]}/username`,
454+
this.config[WabaConfigEnum.RequestTimeout],
455+
null,
456+
);
457+
}
458+
459+
/**
460+
* Get username suggestions for the business phone number.
461+
*
462+
* @see {@link https://developers.facebook.com/documentation/business-messaging/whatsapp/business-phone-numbers/usernames/ | Usernames}
463+
*/
464+
async getUsernameSuggestions(): Promise<phoneNumber.UsernameSuggestionsResponse> {
465+
return this.sendJson(
466+
HttpMethodsEnum.Get,
467+
`${this.config[WabaConfigEnum.PhoneNumberId]}/username_suggestions`,
468+
this.config[WabaConfigEnum.RequestTimeout],
469+
null,
470+
);
471+
}
392472
}

0 commit comments

Comments
 (0)