Problem
The offers delete and campaigns delete commands silently fail or return unexpected responses because the CLI sends a path-based DELETE /offer/{id} request with no body, while the Voluum API requires a batch delete pattern: DELETE /offer with IDs in the request body.
Current behavior
voluum offers delete --id abc-123
# sends: DELETE /offer/abc-123 (no body)
# Voluum API returns 4xx or unexpected response
voluum campaigns delete --id xyz-456
# sends: DELETE /campaign/xyz-456 (no body)
# Voluum API returns 4xx or unexpected response
What the API actually expects
# DELETE /offer with body {"ids": ["abc-123"]}
curl -X DELETE https://api.voluum.com/offer \
-H "cwauth-token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ids": ["abc-123"]}'
# DELETE /campaign with body {"ids": ["xyz-456"]}
curl -X DELETE https://api.voluum.com/campaign \
-H "cwauth-token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ids": ["xyz-456"]}'
Root cause
Three things are wrong in the current implementation:
1. VoluumClient.delete() has no body parameter
// src/client/VoluumClient.ts
async delete<T>(path: string, query?: QueryParams): Promise<T> {
return this.request<T>("DELETE", path, { query });
// ↑ no body — cannot send {"ids": [...]}
}
2. deletePath returns a per-resource path instead of the collection path
// src/endpoints.ts
offers: {
deletePath: (id: string) => `/offer/${encodeURIComponent(id)}`, // ❌ wrong path
},
campaigns: {
deletePath: (id: string) => `/campaign/${encodeURIComponent(id)}`, // ❌ wrong path
},
3. The delete commands send no body
// src/commands/offers.ts (same pattern in campaigns.ts)
const response = await context.client.delete<unknown>(ENDPOINTS.offers.deletePath(options.id));
// ↑ path has ID baked in, no body sent
Proposed Solution
1. Add optional body to VoluumClient.delete()
// src/client/VoluumClient.ts
async delete<T>(path: string, body?: unknown, query?: QueryParams): Promise<T> {
return this.request<T>("DELETE", path, { body, query });
}
2. Update deletePath to the collection path for affected resources
// src/endpoints.ts
offers: {
deletePath: "/offer", // collection path — IDs go in the body
},
campaigns: {
deletePath: "/campaign",
},
Note: deletePath type changes from (id: string) => string to string, consistent with listPath and createPath. Other resources (landers, flows, traffic-sources, affiliate-networks, tracker-domains) should be verified against the API to determine if they share the same batch-body pattern.
3. Add --ids flag for single and batch deletion
Replace the current --id flag with --ids (accepting a comma-separated list) so a single call can delete one or many resources:
// src/commands/offers.ts
const ids = options.ids.split(",").map((s) => s.trim()).filter(Boolean);
const response = await context.client.delete<unknown>(
ENDPOINTS.offers.deletePath,
{ ids },
);
// src/commands/campaigns.ts
const ids = options.ids.split(",").map((s) => s.trim()).filter(Boolean);
const response = await context.client.delete<unknown>(
ENDPOINTS.campaigns.deletePath,
{ ids },
);
Example usage:
# Single delete
voluum offers delete --ids abc-123
# Batch delete
voluum offers delete --ids abc-123,def-456,ghi-789
Test Plan
Affected Files
src/client/VoluumClient.ts — add body param to delete()
src/endpoints.ts — update deletePath shape for offers and campaigns (and any other batch-body resources)
src/commands/offers.ts — replace --id with --ids, send {"ids": [...]} body
src/commands/campaigns.ts — replace --id with --ids, send {"ids": [...]} body
tests/client/VoluumClient.test.ts — update delete tests
tests/commands/offers.test.ts — update delete tests
tests/commands/campaigns.test.ts — update delete tests
Problem
The
offers deleteandcampaigns deletecommands silently fail or return unexpected responses because the CLI sends a path-basedDELETE /offer/{id}request with no body, while the Voluum API requires a batch delete pattern:DELETE /offerwith IDs in the request body.Current behavior
What the API actually expects
Root cause
Three things are wrong in the current implementation:
1.
VoluumClient.delete()has no body parameter2.
deletePathreturns a per-resource path instead of the collection path3. The delete commands send no body
Proposed Solution
1. Add optional body to
VoluumClient.delete()2. Update
deletePathto the collection path for affected resources3. Add
--idsflag for single and batch deletionReplace the current
--idflag with--ids(accepting a comma-separated list) so a single call can delete one or many resources:Example usage:
Test Plan
VoluumClientunit tests to coverdelete()with a bodyoffers deletetests: verifyDELETE /offeris called with{"ids": ["<id>"]}campaigns deletetests: verifyDELETE /campaignis called with{"ids": ["<id>"]}landers,flows, etc.) against the API and update accordinglyvoluum offers delete --ids <real-id>returns successvoluum offers delete --ids <id1>,<id2>deletes both in a single requestAffected Files
src/client/VoluumClient.ts— add body param todelete()src/endpoints.ts— updatedeletePathshape for offers and campaigns (and any other batch-body resources)src/commands/offers.ts— replace--idwith--ids, send{"ids": [...]}bodysrc/commands/campaigns.ts— replace--idwith--ids, send{"ids": [...]}bodytests/client/VoluumClient.test.ts— update delete teststests/commands/offers.test.ts— update delete teststests/commands/campaigns.test.ts— update delete tests