{"id":24372,"date":"2026-08-07T20:54:53","date_gmt":"2026-08-07T20:54:53","guid":{"rendered":"https:\/\/couponaffiliates.com\/?page_id=24372"},"modified":"2026-08-26T12:17:11","modified_gmt":"2026-08-26T12:17:11","slug":"api-docs","status":"publish","type":"page","link":"https:\/\/couponaffiliates.com\/de\/api-docs\/","title":{"rendered":"API Documentation"},"content":{"rendered":"\t<div class=\"rad-docs\" id=\"rad-docs\">\n\t\t<button type=\"button\" class=\"rad-menu-toggle\" aria-expanded=\"false\" aria-controls=\"rad-sidebar\">\n\t\t\t<span class=\"rad-menu-toggle-bars\" aria-hidden=\"true\"><\/span>\n\t\t\tMenu\t\t<\/button>\n\n\t\t<aside class=\"rad-sidebar\" id=\"rad-sidebar\">\n\t\t\t<div class=\"rad-search\">\n\t\t\t\t<svg class=\"rad-search-icon\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\"><circle cx=\"9\" cy=\"9\" r=\"6\" stroke=\"currentColor\" stroke-width=\"2\"\/><path d=\"m14 14 4 4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\/><\/svg>\n\t\t\t\t<input type=\"search\" id=\"rad-search-input\" placeholder=\"Search docs\u2026\" autocomplete=\"off\" aria-label=\"Search documentation\">\n\t\t\t\t<div class=\"rad-search-results\" id=\"rad-search-results\" hidden><\/div>\n\t\t\t<\/div>\n\t\t\t<nav class=\"rad-nav\" aria-label=\"Documentation\">\n\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-cat\">\n\t\t\t\t\t\t<span class=\"rad-nav-cat-label\">Getting Started<\/span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link is-active\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=introduction\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"introduction\"\n\t\t\t\t\t\t\t\taria-current=\"page\">Introduction<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"introduction\" >\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=introduction#what-you-can-do-with-it\" data-rad-page=\"introduction\" data-rad-anchor=\"what-you-can-do-with-it\">What you can do with it<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=introduction#who-can-use-it\" data-rad-page=\"introduction\" data-rad-anchor=\"who-can-use-it\">Who can use it<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=introduction#requirements\" data-rad-page=\"introduction\" data-rad-anchor=\"requirements\">Requirements<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=enabling-the-api\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"enabling-the-api\"\n\t\t\t\t\t\t\t\t>Enabling the API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"enabling-the-api\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=enabling-the-api#turning-individual-endpoints-on-and-off\" data-rad-page=\"enabling-the-api\" data-rad-anchor=\"turning-individual-endpoints-on-and-off\">Turning individual endpoints on and off<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=enabling-the-api#what-the-master-switch-controls\" data-rad-page=\"enabling-the-api\" data-rad-anchor=\"what-the-master-switch-controls\">What the master switch controls<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"authentication\"\n\t\t\t\t\t\t\t\t>Authentication<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"authentication\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication#application-passwords\" data-rad-page=\"authentication\" data-rad-anchor=\"application-passwords\">Application passwords<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication#api-keys\" data-rad-page=\"authentication\" data-rad-anchor=\"api-keys\">API keys<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication#scopes\" data-rad-page=\"authentication\" data-rad-anchor=\"scopes\">Scopes<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication#cookie-authentication\" data-rad-page=\"authentication\" data-rad-anchor=\"cookie-authentication\">Cookie authentication<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication#https\" data-rad-page=\"authentication\" data-rad-anchor=\"https\">HTTPS<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication#failed-attempt-lockout\" data-rad-page=\"authentication\" data-rad-anchor=\"failed-attempt-lockout\">Failed-attempt lockout<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication#who-am-i\" data-rad-page=\"authentication\" data-rad-anchor=\"who-am-i\">Who am I?<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"requests-responses\"\n\t\t\t\t\t\t\t\t>Requests &amp; Responses<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"requests-responses\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses#conventions\" data-rad-page=\"requests-responses\" data-rad-anchor=\"conventions\">Conventions<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses#pagination\" data-rad-page=\"requests-responses\" data-rad-anchor=\"pagination\">Pagination<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses#dates\" data-rad-page=\"requests-responses\" data-rad-anchor=\"dates\">Dates<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses#errors\" data-rad-page=\"requests-responses\" data-rad-anchor=\"errors\">Errors<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses#rate-limiting\" data-rad-page=\"requests-responses\" data-rad-anchor=\"rate-limiting\">Rate limiting<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=performance-caching\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"performance-caching\"\n\t\t\t\t\t\t\t\t>Performance &amp; Caching<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"performance-caching\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=performance-caching#what-is-cached\" data-rad-page=\"performance-caching\" data-rad-anchor=\"what-is-cached\">What is cached<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=performance-caching#throttle-responses\" data-rad-page=\"performance-caching\" data-rad-anchor=\"throttle-responses\">Throttle responses<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=performance-caching#working-efficiently\" data-rad-page=\"performance-caching\" data-rad-anchor=\"working-efficiently\">Working efficiently<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"security-disclaimers\"\n\t\t\t\t\t\t\t\t>Security &amp; Disclaimers<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"security-disclaimers\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#your-responsibilities\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"your-responsibilities\">Your responsibilities<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#personal-data\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"personal-data\">Personal data<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#money-and-financial-records\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"money-and-financial-records\">Money and financial records<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#treat-inbound-webhooks-as-untrusted\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"treat-inbound-webhooks-as-untrusted\">Treat inbound webhooks as untrusted<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#third-party-and-ai-services\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"third-party-and-ai-services\">Third-party and AI services<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#stability-and-support\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"stability-and-support\">Stability and support<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#no-warranty\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"no-warranty\">No warranty<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers#licensing\" data-rad-page=\"security-disclaimers\" data-rad-anchor=\"licensing\">Licensing<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=endpoint-index\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"endpoint-index\"\n\t\t\t\t\t\t\t\t>Endpoint Index<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-cat\">\n\t\t\t\t\t\t<span class=\"rad-nav-cat-label\">Endpoints<\/span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=me\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"me\"\n\t\t\t\t\t\t\t\t>Me<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=affiliates\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"affiliates\"\n\t\t\t\t\t\t\t\t>Affiliates<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"affiliates\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=affiliates#list-affiliates\" data-rad-page=\"affiliates\" data-rad-anchor=\"list-affiliates\">List affiliates<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=affiliates#get-one-affiliate\" data-rad-page=\"affiliates\" data-rad-anchor=\"get-one-affiliate\">Get one affiliate<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=affiliates#affiliate-stats\" data-rad-page=\"affiliates\" data-rad-anchor=\"affiliate-stats\">Affiliate stats<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=coupons\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"coupons\"\n\t\t\t\t\t\t\t\t>Coupons<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"coupons\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=coupons#list-coupons\" data-rad-page=\"coupons\" data-rad-anchor=\"list-coupons\">List coupons<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=coupons#get-one-coupon\" data-rad-page=\"coupons\" data-rad-anchor=\"get-one-coupon\">Get one coupon<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=coupons#coupon-stats\" data-rad-page=\"coupons\" data-rad-anchor=\"coupon-stats\">Coupon stats<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=coupons#referred-orders\" data-rad-page=\"coupons\" data-rad-anchor=\"referred-orders\">Referred orders<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=payouts\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"payouts\"\n\t\t\t\t\t\t\t\t>Payouts<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"payouts\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=payouts#list-payouts\" data-rad-page=\"payouts\" data-rad-anchor=\"list-payouts\">List payouts<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=payouts#get-one-payout\" data-rad-page=\"payouts\" data-rad-anchor=\"get-one-payout\">Get one payout<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=payouts#request-a-payout\" data-rad-page=\"payouts\" data-rad-anchor=\"request-a-payout\">Request a payout<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=payouts#update-payout-status\" data-rad-page=\"payouts\" data-rad-anchor=\"update-payout-status\">Update payout status<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=registrations\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"registrations\"\n\t\t\t\t\t\t\t\t>Registrations<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"registrations\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=registrations#list-registrations\" data-rad-page=\"registrations\" data-rad-anchor=\"list-registrations\">List registrations<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=registrations#get-one-registration\" data-rad-page=\"registrations\" data-rad-anchor=\"get-one-registration\">Get one registration<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=registrations#approve-or-decline\" data-rad-page=\"registrations\" data-rad-anchor=\"approve-or-decline\">Approve or decline<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=clicks\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"clicks\"\n\t\t\t\t\t\t\t\t>Clicks<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=events\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"events\"\n\t\t\t\t\t\t\t\t>Events<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"events\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=events#event-types\" data-rad-page=\"events\" data-rad-anchor=\"event-types\">Event types<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reports\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"reports\"\n\t\t\t\t\t\t\t\t>Reports<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=api-key-management\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"api-key-management\"\n\t\t\t\t\t\t\t\t>API Key Management<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"api-key-management\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=api-key-management#list-keys\" data-rad-page=\"api-key-management\" data-rad-anchor=\"list-keys\">List keys<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=api-key-management#create-a-key\" data-rad-page=\"api-key-management\" data-rad-anchor=\"create-a-key\">Create a key<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=api-key-management#revoke-a-key\" data-rad-page=\"api-key-management\" data-rad-anchor=\"revoke-a-key\">Revoke a key<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-cat\">\n\t\t\t\t\t\t<span class=\"rad-nav-cat-label\">Webhooks<\/span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhooks-overview\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"webhooks-overview\"\n\t\t\t\t\t\t\t\t>Webhooks Overview<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"webhooks-overview\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhooks-overview#available-events\" data-rad-page=\"webhooks-overview\" data-rad-anchor=\"available-events\">Available events<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhooks-overview#endpoint-requirements\" data-rad-page=\"webhooks-overview\" data-rad-anchor=\"endpoint-requirements\">Endpoint requirements<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=deliveries-security\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"deliveries-security\"\n\t\t\t\t\t\t\t\t>Deliveries &amp; Security<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"deliveries-security\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=deliveries-security#payload-format\" data-rad-page=\"deliveries-security\" data-rad-anchor=\"payload-format\">Payload format<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=deliveries-security#payload-shapes-by-event\" data-rad-page=\"deliveries-security\" data-rad-anchor=\"payload-shapes-by-event\">Payload shapes by event<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=deliveries-security#verifying-signatures\" data-rad-page=\"deliveries-security\" data-rad-anchor=\"verifying-signatures\">Verifying signatures<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=deliveries-security#retries-and-automatic-disabling\" data-rad-page=\"deliveries-security\" data-rad-anchor=\"retries-and-automatic-disabling\">Retries and automatic disabling<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"webhook-management-api\"\n\t\t\t\t\t\t\t\t>Webhook Management API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"webhook-management-api\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api#list-webhooks\" data-rad-page=\"webhook-management-api\" data-rad-anchor=\"list-webhooks\">List webhooks<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api#create-a-webhook\" data-rad-page=\"webhook-management-api\" data-rad-anchor=\"create-a-webhook\">Create a webhook<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api#update-a-webhook\" data-rad-page=\"webhook-management-api\" data-rad-anchor=\"update-a-webhook\">Update a webhook<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api#test-a-webhook\" data-rad-page=\"webhook-management-api\" data-rad-anchor=\"test-a-webhook\">Test a webhook<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api#delete-a-webhook\" data-rad-page=\"webhook-management-api\" data-rad-anchor=\"delete-a-webhook\">Delete a webhook<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api#event-catalog\" data-rad-page=\"webhook-management-api\" data-rad-anchor=\"event-catalog\">Event catalog<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-cat\">\n\t\t\t\t\t\t<span class=\"rad-nav-cat-label\">Integrations<\/span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"code-samples\"\n\t\t\t\t\t\t\t\t>Code Samples<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"code-samples\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples#php-inside-wordpress\" data-rad-page=\"code-samples\" data-rad-anchor=\"php-inside-wordpress\">PHP (inside WordPress)<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples#php-standalone-with-pagination\" data-rad-page=\"code-samples\" data-rad-anchor=\"php-standalone-with-pagination\">PHP (standalone, with pagination)<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples#javascript-node\" data-rad-page=\"code-samples\" data-rad-anchor=\"javascript-node\">JavaScript \/ Node<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples#python\" data-rad-page=\"code-samples\" data-rad-anchor=\"python\">Python<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples#command-line\" data-rad-page=\"code-samples\" data-rad-anchor=\"command-line\">Command line<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=no-code-platforms\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"no-code-platforms\"\n\t\t\t\t\t\t\t\t>No-Code Platforms<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"no-code-platforms\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=no-code-platforms#push-webhook-trigger-recommended-pro\" data-rad-page=\"no-code-platforms\" data-rad-anchor=\"push-webhook-trigger-recommended-pro\">Push: webhook trigger (recommended, PRO)<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=no-code-platforms#pull-scheduled-polling\" data-rad-page=\"no-code-platforms\" data-rad-anchor=\"pull-scheduled-polling\">Pull: scheduled polling<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=no-code-platforms#writing-back\" data-rad-page=\"no-code-platforms\" data-rad-anchor=\"writing-back\">Writing back<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reporting-dashboards\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"reporting-dashboards\"\n\t\t\t\t\t\t\t\t>Reporting &amp; Dashboards<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"reporting-dashboards\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reporting-dashboards#google-sheets\" data-rad-page=\"reporting-dashboards\" data-rad-anchor=\"google-sheets\">Google Sheets<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reporting-dashboards#looker-studio-power-bi-metabase\" data-rad-page=\"reporting-dashboards\" data-rad-anchor=\"looker-studio-power-bi-metabase\">Looker Studio, Power BI, Metabase<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reporting-dashboards#warehousing-the-data\" data-rad-page=\"reporting-dashboards\" data-rad-anchor=\"warehousing-the-data\">Warehousing the data<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=notifications\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"notifications\"\n\t\t\t\t\t\t\t\t>Notifications<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=ai-agents-assistants\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"ai-agents-assistants\"\n\t\t\t\t\t\t\t\t>AI Agents &amp; Assistants<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"ai-agents-assistants\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=ai-agents-assistants#setting-one-up\" data-rad-page=\"ai-agents-assistants\" data-rad-anchor=\"setting-one-up\">Setting one up<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=ai-agents-assistants#prompting-notes\" data-rad-page=\"ai-agents-assistants\" data-rad-anchor=\"prompting-notes\">Prompting notes<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=ai-agents-assistants#safety\" data-rad-page=\"ai-agents-assistants\" data-rad-anchor=\"safety\">Safety<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"recipes\"\n\t\t\t\t\t\t\t\t>Recipes<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"recipes\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#sync-new-referrals-into-another-system\" data-rad-page=\"recipes\" data-rad-anchor=\"sync-new-referrals-into-another-system\">Sync new referrals into another system<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#build-a-monthly-affiliate-statement\" data-rad-page=\"recipes\" data-rad-anchor=\"build-a-monthly-affiliate-statement\">Build a monthly affiliate statement<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#auto-approve-applications-that-meet-your-criteria\" data-rad-page=\"recipes\" data-rad-anchor=\"auto-approve-applications-that-meet-your-criteria\">Auto-approve applications that meet your criteria<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#give-an-affiliate-read-only-api-access-to-their-own-data\" data-rad-page=\"recipes\" data-rad-anchor=\"give-an-affiliate-read-only-api-access-to-their-own-data\">Give an affiliate read-only API access to their own data<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#reconcile-payouts-with-your-accounting-system\" data-rad-page=\"recipes\" data-rad-anchor=\"reconcile-payouts-with-your-accounting-system\">Reconcile payouts with your accounting system<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#flag-a-payout-fraud-pattern\" data-rad-page=\"recipes\" data-rad-anchor=\"flag-a-payout-fraud-pattern\">Flag a payout-fraud pattern<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#refresh-stale-coupon-statistics-nightly\" data-rad-page=\"recipes\" data-rad-anchor=\"refresh-stale-coupon-statistics-nightly\">Refresh stale coupon statistics nightly<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes#check-the-health-of-your-integration\" data-rad-page=\"recipes\" data-rad-anchor=\"check-the-health-of-your-integration\">Check the health of your integration<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-cat\">\n\t\t\t\t\t\t<span class=\"rad-nav-cat-label\">Developer Reference<\/span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=openapi-document\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"openapi-document\"\n\t\t\t\t\t\t\t\t>OpenAPI Document<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"openapi-document\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=openapi-document#fetching-the-spec\" data-rad-page=\"openapi-document\" data-rad-anchor=\"fetching-the-spec\">Fetching the spec<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=openapi-document#what-it-contains\" data-rad-page=\"openapi-document\" data-rad-anchor=\"what-it-contains\">What it contains<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=availability\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"availability\"\n\t\t\t\t\t\t\t\t>Availability<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=error-reference\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"error-reference\"\n\t\t\t\t\t\t\t\t>Error Reference<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=hooks-filters\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"hooks-filters\"\n\t\t\t\t\t\t\t\t>Hooks &amp; Filters<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"hooks-filters\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=hooks-filters#behaviour-filters\" data-rad-page=\"hooks-filters\" data-rad-anchor=\"behaviour-filters\">Behaviour filters<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=hooks-filters#response-filters\" data-rad-page=\"hooks-filters\" data-rad-anchor=\"response-filters\">Response filters<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=hooks-filters#actions\" data-rad-page=\"hooks-filters\" data-rad-anchor=\"actions\">Actions<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-link\"\n\t\t\t\t\t\t\t\thref=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=legacy-v1-api\"\n\t\t\t\t\t\t\t\tdata-rad-page=\"legacy-v1-api\"\n\t\t\t\t\t\t\t\t>Legacy v1 API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"rad-nav-subs\" data-rad-subs=\"legacy-v1-api\" hidden>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-nav-sub\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=legacy-v1-api#how-v1-relates-to-v2\" data-rad-page=\"legacy-v1-api\" data-rad-anchor=\"how-v1-relates-to-v2\">How v1 relates to v2<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<\/nav>\n\t\t<\/aside>\n\n\t\t<main class=\"rad-main\">\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-introduction\" data-rad-page=\"introduction\" data-rad-title=\"Introduction\" >\n\t\t\t\t\t<span class=\"rad-page-cat\">Getting Started<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Introduction<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>The Coupon Affiliates REST API gives external tools, scripts and AI agents structured JSON access to your affiliate program: affiliates, coupons, referred orders, commission, payouts, registrations, click tracking, a change-event feed and store-wide reports. It also delivers outbound <strong>webhooks<\/strong> (PRO), so other systems can react the moment something happens in your program. On the free version the same ground is covered by polling the <a href=\"#\">change-event feed<\/a>, which carries a cursor for exactly that.<\/p>\r\n<p>Everything lives under one base URL on your own site:<\/p>\r\n<pre><code>https:\/\/example.com\/wp-json\/wcusage\/v2<\/code><\/pre>\r\n<p>A first request, using a WordPress application password for an admin user:<\/p>\r\n<pre><code>curl https:\/\/example.com\/wp-json\/wcusage\/v2\/me \\\r\n  -u \"admin:abcd efgh ijkl mnop qrst uvwx\"<\/code><\/pre>\r\n<pre><code>{\r\n  \"user\": { \"id\": 1, \"display_name\": \"Admin\", \"login\": \"admin\", \"email\": \"admin@example.com\" },\r\n  \"is_admin\": true,\r\n  \"is_affiliate\": false,\r\n  \"auth\": { \"method\": \"wordpress\", \"key_id\": null, \"scopes\": null },\r\n  \"coupons\": [],\r\n  \"api_version\": \"8.2.0\"\r\n}<\/code><\/pre>\r\n\r\n<h3 id=\"what-you-can-do-with-it\">What you can do with it<\/h3>\r\n<ul>\r\n<li><strong>Report<\/strong> \u2014 pull program totals, per-affiliate stats, per-coupon sales and commission, and referred-order lists into a BI tool, spreadsheet or custom dashboard.<\/li>\r\n<li><strong>Automate<\/strong> \u2014 approve or decline affiliate applications, create payout requests, and react to program events in real time.<\/li>\r\n<li><strong>Integrate<\/strong> \u2014 connect Zapier\/Make-style automation, a CRM, an accounting system, or an AI agent that answers questions about your affiliates.<\/li>\r\n<li><strong>Build affiliate-facing tools<\/strong> \u2014 an affiliate can authenticate as themselves and read only their own data, so you can build a custom affiliate app without handing out admin access.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"who-can-use-it\">Who can use it<\/h3>\r\n<ul>\r\n<li><strong>Admins<\/strong> \u2014 any user who passes the plugin's admin access check (usually <code>manage_woocommerce<\/code>, or whichever capability you configured) can read and manage everything.<\/li>\r\n<li><strong>Affiliates<\/strong> \u2014 can read their own coupons, stats, referred orders, click stats and payouts, and can request a payout for their own coupon. They cannot see other affiliates, store-wide reports or the events feed.<\/li>\r\n<li><strong>Multi-level uplines<\/strong> (PRO) \u2014 can also read a downline affiliate's coupon, stats and referred orders.<\/li>\r\n<li><strong>Nobody else.<\/strong> There are no anonymous endpoints other than the OpenAPI document.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"requirements\">Requirements<\/h3>\r\n<ul>\r\n<li>Coupon Affiliates <strong>8.2 or later<\/strong>, with the API switched on (see the next page).<\/li>\r\n<li>The WordPress REST API enabled \u2014 it is by default.<\/li>\r\n<li><strong>Pretty permalinks<\/strong>, if you want to use the <code>\/wp-json\/\u2026<\/code> URL form. On a site with plain permalinks, use <code>https:\/\/example.com\/?rest_route=\/wcusage\/v2\/me<\/code> instead: the <code>\/wp-json\/<\/code> path is not a REST request at all on such a site, and API keys will not authenticate against it.<\/li>\r\n<li><strong>HTTPS.<\/strong> API keys are refused over plain HTTP outside local and development environments.<\/li>\r\n<li>Some endpoints depend on features that may not be present (payouts, registrations, clicks, activity log). Those answer <code>501<\/code> rather than pretending to be empty \u2014 see <a href=\"#\">Availability<\/a>.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">Before connecting anything to a live store, read <a href=\"#\">Security &amp; Disclaimers<\/a>. This API returns personal data about real people and can perform actions that cannot be undone through the API \u2014 and on some configurations, actions that move real money.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span><\/span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=enabling-the-api\" data-rad-page=\"enabling-the-api\"><span>Next<\/span>Enabling the API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-enabling-the-api\" data-rad-page=\"enabling-the-api\" data-rad-title=\"Enabling the API\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Getting Started<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Enabling the API<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>The API ships <strong>switched off<\/strong>. A site never starts answering API requests just because the plugin was updated \u2014 an administrator has to turn it on deliberately.<\/p>\r\n<ol>\r\n<li>Go to <strong>Coupon Affiliates \u2192 Admin Tools \u2192 API<\/strong> in wp-admin.<\/li>\r\n<li>Press <strong>Enable API<\/strong> at the top of the page.<\/li>\r\n<li>Create an API key, or use a WordPress application password.<\/li>\r\n<\/ol>\r\n<p>The same screen shows your base URL, the OpenAPI URL, every registered endpoint with the access level it needs, your API keys, and \u2014 on PRO \u2014 your webhooks.<\/p>\r\n\r\n<h3 id=\"turning-individual-endpoints-on-and-off\">Turning individual endpoints on and off<\/h3>\r\n<p>Each endpoint has its own checkbox on the API screen. Unticking one removes it from the route table for every request \u2014 it answers <code>404<\/code> with the core code <code>rest_no_route<\/code>, exactly as though it had never been registered. This is the right tool for narrowing what any integration can reach at all, regardless of who authenticates.<\/p>\r\n<p>The settings live in the <code>wcusage_api_settings<\/code> option. Only the exceptions are stored, so an endpoint added in a later release is on by default once the API itself is on.<\/p>\r\n\r\n<h3 id=\"what-the-master-switch-controls\">What the master switch controls<\/h3>\r\n<table><thead><tr><th>Behaviour<\/th><th>API on<\/th><th>API off<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>wcusage\/v2<\/code> routes<\/td><td>Registered<\/td><td>Not registered (<code>404<\/code>)<\/td><\/tr>\r\n<tr><td>API key authentication<\/td><td>Works<\/td><td>Refused everywhere, v1 included<\/td><\/tr>\r\n<tr><td>Webhook deliveries (PRO)<\/td><td>Queued and sent<\/td><td>Not queued; already-queued retries are dropped<\/td><\/tr>\r\n<tr><td>Adding and testing webhooks in wp-admin (PRO)<\/td><td>Works<\/td><td>Works<\/td><\/tr>\r\n<tr><td>Legacy <code>woo-coupon-usage\/v1<\/code> routes<\/td><td>Available<\/td><td>Still available (application password or cookie auth only)<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">Disabling the API stops outbound webhook deliveries as well as inbound requests. If a system depends on webhooks, switching the API off silently stops feeding it.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=introduction\" data-rad-page=\"introduction\"><span>Previous<\/span>Introduction<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication\" data-rad-page=\"authentication\"><span>Next<\/span>Authentication<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-authentication\" data-rad-page=\"authentication\" data-rad-title=\"Authentication\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Getting Started<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Authentication<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Every endpoint except <code>\/openapi<\/code> requires an authenticated WordPress user. Three methods work; each resolves to a user, and that user's capabilities decide what the request may access.<\/p>\r\n\r\n<h3 id=\"application-passwords\">Application passwords<\/h3>\r\n<p>Built into WordPress. Create one under <strong>Users \u2192 Profile \u2192 Application Passwords<\/strong>, then send it with HTTP Basic auth:<\/p>\r\n<pre><code>curl https:\/\/example.com\/wp-json\/wcusage\/v2\/affiliates \\\r\n  -u \"admin:abcd efgh ijkl mnop qrst uvwx\"<\/code><\/pre>\r\n<p>An application password carries the user's <strong>full<\/strong> permissions, on this API and on every other WordPress endpoint. It is the quickest way to get started, and fine for a trusted server-to-server integration you control.<\/p>\r\n\r\n<h3 id=\"api-keys\">API keys<\/h3>\r\n<p>Plugin API keys are the better choice for anything you do not fully control, because they can be scoped, expired and revoked without touching the WordPress account. Create one under <strong>Coupon Affiliates \u2192 Admin Tools \u2192 API<\/strong>, or through the <a href=\"#\">key management endpoints<\/a>.<\/p>\r\n<p>A key looks like <code>wcus_9f2c1e4a\u2026<\/code> and is shown <strong>once<\/strong>, at creation \u2014 only a SHA-256 hash is stored. Send it as a bearer token:<\/p>\r\n<pre><code>curl https:\/\/example.com\/wp-json\/wcusage\/v2\/me \\\r\n  -H \"Authorization: Bearer wcus_9f2c1e4a7b3d5e6f8a9b0c1d2e3f4a5b6c7d8e9f\"<\/code><\/pre>\r\n<p>If your client cannot set the <code>Authorization<\/code> header (some hosts strip it), an alternative header is accepted:<\/p>\r\n<pre><code>X-WCUsage-API-Key: wcus_9f2c1e4a7b3d5e6f8a9b0c1d2e3f4a5b6c7d8e9f<\/code><\/pre>\r\n<p>Every key:<\/p>\r\n<ul>\r\n<li><strong>Acts as one WordPress user.<\/strong> A key created against an affiliate's account can only ever reach that affiliate's own data, whatever it asks for. A key against an admin account has admin access.<\/li>\r\n<li><strong>Carries scopes<\/strong> that restrict it further, on top of that user's capabilities.<\/li>\r\n<li><strong>Works only on this plugin's namespaces<\/strong> \u2014 <code>wcusage\/v2<\/code> and <code>woo-coupon-usage\/v1<\/code>. Presenting one to a core or WooCommerce endpoint fails with <code>403 wcusage_api_key_wrong_namespace<\/code>. An API key is not a general-purpose WordPress credential.<\/li>\r\n<li>Can have an optional <strong>expiry date<\/strong>, and can be revoked at any moment. Revocation takes effect on the very next request.<\/li>\r\n<li>Records a <strong>last used<\/strong> timestamp, updated at most once every five minutes \u2014 a liveness signal, not an audit log.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"scopes\">Scopes<\/h3>\r\n<table><thead><tr><th>Scope<\/th><th>Grants<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>read<\/code><\/td><td>Every GET endpoint: affiliates, coupons, stats, referred orders, payouts, registrations, clicks, events, reports.<\/td><\/tr>\r\n<tr><td><code>write<\/code><\/td><td>Creating and changing data: payout requests, payout status changes, registration approval and decline, and refreshing stored coupon stats.<\/td><\/tr>\r\n<tr><td><code>manage<\/code><\/td><td>Managing the API itself: creating and revoking API keys, and \u2014 on PRO \u2014 creating, editing, testing and deleting webhooks.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\"><code>manage<\/code> is not a peer of the other two. A key holding it can issue itself another key with any scope, and can point a webhook at a server of its choosing. Treat it as full access for the user behind the key, and give it only to integrations that genuinely administer the API.<\/div><\/div>\r\n<p>Scopes apply <strong>only<\/strong> to API keys. With an application password or a logged-in cookie, <code>auth.scopes<\/code> is <code>null<\/code> and access is governed purely by capabilities. A key missing a required scope receives <code>403<\/code> with the code <code>wcusage_api_insufficient_scope<\/code>.<\/p>\r\n<p>On the legacy <code>woo-coupon-usage\/v1<\/code> namespace, which predates scopes, keys are held to a fail-closed default: <code>GET<\/code>, <code>HEAD<\/code> and <code>OPTIONS<\/code> need <code>read<\/code>, anything else needs <code>write<\/code>.<\/p>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">When connecting an AI agent or a third-party SaaS, create a dedicated <strong>read-only key on a dedicated account<\/strong>. It cannot change anything on your site whatever the client does, and you can revoke it without disturbing anything else.<\/div><\/div>\r\n\r\n<h3 id=\"cookie-authentication\">Cookie authentication<\/h3>\r\n<p>Same-site JavaScript can use the logged-in cookie with a REST nonce, exactly as with any WordPress REST endpoint:<\/p>\r\n<pre><code>fetch( '\/wp-json\/wcusage\/v2\/me', {\r\n  headers: { 'X-WP-Nonce': wpApiSettings.nonce },\r\n  credentials: 'same-origin'\r\n} ).then( r =&gt; r.json() );<\/code><\/pre>\r\n\r\n<h3 id=\"https\">HTTPS<\/h3>\r\n<p>Bearer tokens are credentials, so they are refused over plain HTTP unless <code>wp_get_environment_type()<\/code> reports <code>local<\/code> or <code>development<\/code>. Such a request fails with <code>401 wcusage_api_https_required<\/code>. The <code>wcusage_api_require_https<\/code> filter can override it \u2014 do that only if you know exactly why.<\/p>\r\n\r\n<h3 id=\"failed-attempt-lockout\">Failed-attempt lockout<\/h3>\r\n<p>An address that presents <strong>20 invalid keys within 15 minutes<\/strong> is refused further attempts with <code>429 wcusage_api_too_many_auth_failures<\/code> until the window rolls over. Counts are kept per IP address, so one client cannot lock out another. Adjust with the <code>wcusage_api_max_auth_failures<\/code> filter.<\/p>\r\n\r\n<h3 id=\"who-am-i\">Who am I?<\/h3>\r\n<p>Whatever method you use, <code>GET \/me<\/code> reports exactly what the request is authenticated as, what access level it has, and which scopes are in force. Call it first when setting up any integration.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=enabling-the-api\" data-rad-page=\"enabling-the-api\"><span>Previous<\/span>Enabling the API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses\" data-rad-page=\"requests-responses\"><span>Next<\/span>Requests &amp; Responses<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-requests-responses\" data-rad-page=\"requests-responses\" data-rad-title=\"Requests &amp; Responses\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Getting Started<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Requests &amp; Responses<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n\r\n<h3 id=\"conventions\">Conventions<\/h3>\r\n<ul>\r\n<li>All responses are JSON. <code>GET<\/code> parameters go in the query string; <code>POST<\/code> and <code>PATCH<\/code> parameters go in a JSON body with <code>Content-Type: application\/json<\/code> (WordPress also accepts form-encoded bodies).<\/li>\r\n<li>Resources are addressed by <strong>numeric ID<\/strong>. Coupon codes are never identifiers: WooCommerce coupon codes are case-insensitive and are not guaranteed unique.<\/li>\r\n<li>Money values are plain JSON numbers in the store's currency \u2014 no symbol, no thousands separators. <code>GET \/reports\/summary<\/code> reports the currency code.<\/li>\r\n<li>Collections are returned as a bare JSON array, with totals in response headers.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"pagination\">Pagination<\/h3>\r\n<p>Every collection endpoint takes the same two parameters:<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>page<\/td><td>integer<\/td><td>Page number, starting at 1. Default <code>1<\/code>.<\/td><\/tr>\r\n<tr><td>per_page<\/td><td>integer<\/td><td>Items per page, 1\u2013100. Default <code>20<\/code>.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>And returns the same two headers:<\/p>\r\n<ul>\r\n<li><code>X-WP-Total<\/code> \u2014 total matching items.<\/li>\r\n<li><code>X-WP-TotalPages<\/code> \u2014 total pages at the current <code>per_page<\/code>.<\/li>\r\n<\/ul>\r\n<p>A page past the end of the result set returns an empty array with correct headers, without running the underlying query.<\/p>\r\n\r\n<h3 id=\"dates\">Dates<\/h3>\r\n<ul>\r\n<li><strong>Request parameters<\/strong> (<code>from<\/code>, <code>to<\/code>, <code>expires<\/code>) use <code>Y-m-d<\/code>, e.g. <code>2026-08-01<\/code>. Anything else is rejected with <code>400 rest_invalid_param<\/code>.<\/li>\r\n<li><strong>Response fields<\/strong> use ISO\u00a08601 in the site's timezone, e.g. <code>2026-08-07T14:03:22<\/code>. Empty dates are <code>null<\/code>.<\/li>\r\n<li>Ranges include both ends. When <code>from<\/code> is given without <code>to<\/code>, <code>to<\/code> defaults to <strong>today<\/strong>.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"errors\">Errors<\/h3>\r\n<p>Errors use the standard WordPress REST shape. Every code raised by this plugin is prefixed <code>wcusage_api_<\/code>:<\/p>\r\n<pre><code>{\r\n  \"code\": \"wcusage_api_forbidden\",\r\n  \"message\": \"You do not have permission to access this resource.\",\r\n  \"data\": { \"status\": 403 }\r\n}<\/code><\/pre>\r\n<p>Rate-limit and throttle errors add <code>retry_after<\/code> (seconds) to <code>data<\/code>.<\/p>\r\n<table><thead><tr><th>Status<\/th><th>Meaning<\/th><\/tr><\/thead><tbody>\r\n<tr><td>400<\/td><td>Invalid parameter, or the action is not possible right now (no unpaid balance, status already set, activity log disabled).<\/td><\/tr>\r\n<tr><td>401<\/td><td>Not authenticated, or the API key is invalid, revoked, expired, or was sent over plain HTTP.<\/td><\/tr>\r\n<tr><td>403<\/td><td>Authenticated but not permitted \u2014 wrong capability, missing scope, or a key used outside the plugin's namespaces.<\/td><\/tr>\r\n<tr><td>404<\/td><td>Not found. Also returned for a resource that exists but belongs to somebody else, and for an endpoint switched off in the settings.<\/td><\/tr>\r\n<tr><td>409<\/td><td>Conflict \u2014 another request is mid-flight, or the resource changed underneath this one.<\/td><\/tr>\r\n<tr><td>429<\/td><td>Rate limited, throttled, or locked out after repeated authentication failures.<\/td><\/tr>\r\n<tr><td>500<\/td><td>Server error, e.g. the site could not generate a secure token.<\/td><\/tr>\r\n<tr><td>501<\/td><td>The feature this endpoint reads is not present on this install (payouts add-on inactive, table missing).<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">A resource owned by somebody else answers <code>404<\/code>, not <code>403<\/code>. That is deliberate: it stops an authenticated affiliate walking the ID space to learn which coupons, payouts or affiliates exist. So do not read a <code>404<\/code> as proof that an ID is unused.<\/div><\/div>\r\n<p>A full list of plugin error codes is in the <a href=\"#\">Error reference<\/a>.<\/p>\r\n\r\n<h3 id=\"rate-limiting\">Rate limiting<\/h3>\r\n<p>Requests to both plugin namespaces are counted per minute, per identity \u2014 the API key, the logged-in user, or a shared bucket for anonymous callers:<\/p>\r\n<ul>\r\n<li>Authenticated: <strong>120 requests per minute<\/strong><\/li>\r\n<li>Unauthenticated: <strong>30 requests per minute<\/strong><\/li>\r\n<\/ul>\r\n<p>Over the limit, requests get <code>429 wcusage_api_rate_limited<\/code> with <code>retry_after: 60<\/code>. Malformed requests are counted too, so a client stuck retrying a bad parameter is throttled like any other. Adjust the ceiling with the <code>wcusage_api_rate_limit<\/code> filter.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=authentication\" data-rad-page=\"authentication\"><span>Previous<\/span>Authentication<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=performance-caching\" data-rad-page=\"performance-caching\"><span>Next<\/span>Performance &amp; Caching<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-performance-caching\" data-rad-page=\"performance-caching\" data-rad-title=\"Performance &amp; Caching\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Getting Started<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Performance &amp; Caching<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Most endpoints read cached figures and are cheap enough to poll. A few recalculate from the order history and are deliberately expensive; those are cached and throttled so one client cannot make the database do unbounded work.<\/p>\r\n\r\n<h3 id=\"what-is-cached\">What is cached<\/h3>\r\n<table><thead><tr><th>Endpoint<\/th><th>Behaviour<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>\/coupons\/{id}\/stats<\/code> (all-time)<\/td><td>Reads the stored snapshot \u2014 the same figures the affiliate dashboard shows. One meta read. Reports <code>source: \"cache\"<\/code>.<\/td><\/tr>\r\n<tr><td><code>\/coupons\/{id}\/stats<\/code> (with <code>from<\/code>\/<code>to<\/code>, or <code>refresh=true<\/code>)<\/td><td>Recalculates from the orders. Each range is cached 60 seconds; an uncached calculation is limited to one per coupon per minute.<\/td><\/tr>\r\n<tr><td><code>\/coupons\/{id}\/orders<\/code><\/td><td>The prepared list is cached whole for 60 seconds per coupon + range + status, so paginating through it costs nothing. An uncached combination is limited to one per coupon per minute.<\/td><\/tr>\r\n<tr><td><code>\/affiliates\/{id}\/stats<\/code> (with dates)<\/td><td>Cached 60 seconds per range; uncached calculations limited to one per affiliate per minute.<\/td><\/tr>\r\n<tr><td><code>\/reports\/summary<\/code><\/td><td>Cached 5 minutes per <code>top<\/code> value. <code>refresh=true<\/code> bypasses it.<\/td><\/tr>\r\n<tr><td>Everything else<\/td><td>Answered live from indexed queries.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n\r\n<h3 id=\"throttle-responses\">Throttle responses<\/h3>\r\n<p>When an uncached recalculation is asked for inside the one-per-minute window, the response is <code>429<\/code> with code <code>wcusage_api_throttled<\/code> and <code>retry_after: 60<\/code>. The one exception is all-time coupon stats, which return the stored snapshot with <code>source: \"throttled\"<\/code> rather than failing.<\/p>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">Poll a fixed date range rather than a moving one. A client that keeps asking for <em>yesterday<\/em> is always served from cache; a client that shifts the range slightly on every call forces a fresh scan each minute and will start seeing <code>429<\/code>s.<\/div><\/div>\r\n\r\n<h3 id=\"working-efficiently\">Working efficiently<\/h3>\r\n<ul>\r\n<li>For \"what changed?\", poll <code>\/events?after={id}<\/code> \u2014 one cheap indexed query \u2014 or use webhooks and stop polling altogether.<\/li>\r\n<li>For \"how is the program doing?\", one call to <code>\/reports\/summary<\/code> replaces walking every affiliate.<\/li>\r\n<li>Use <code>per_page=100<\/code> on collections instead of many small pages.<\/li>\r\n<li>Prefer all-time stats (no <code>from<\/code>\/<code>to<\/code>) for frequent reads; ask for a range only when you need one.<\/li>\r\n<\/ul>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=requests-responses\" data-rad-page=\"requests-responses\"><span>Previous<\/span>Requests &amp; Responses<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers\" data-rad-page=\"security-disclaimers\"><span>Next<\/span>Security &amp; Disclaimers<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-security-disclaimers\" data-rad-page=\"security-disclaimers\" data-rad-title=\"Security &amp; Disclaimers\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Getting Started<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Security &amp; Disclaimers<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>The API hands external systems real access to your affiliate program \u2014 personal data, commission figures and, in some configurations, the ability to move money. Please read this page before building anything against it.<\/p>\r\n\r\n<h3 id=\"your-responsibilities\">Your responsibilities<\/h3>\r\n<p>Enabling the API is an explicit decision, and everything done with the credentials you issue is done <strong>on your authority and under your account<\/strong>. In particular, you are responsible for:<\/p>\r\n<ul>\r\n<li><strong>Every key you create.<\/strong> A key acts as the WordPress user you attached it to, with that user's capabilities. Creating a key against an administrator account and pasting it into a third-party service gives that service administrator-level reach over your affiliate data.<\/li>\r\n<li><strong>Where the data goes.<\/strong> Once a response leaves your server, its handling is governed by whoever received it \u2014 the SaaS platform, the spreadsheet, the AI provider \u2014 not by this plugin.<\/li>\r\n<li><strong>What your automations do.<\/strong> Approving applications, cancelling payouts and creating payout requests are all real, consequential actions. The API performs what it is asked to; it cannot know whether the request reflects what you intended.<\/li>\r\n<li><strong>Storing credentials safely.<\/strong> Keys and webhook secrets belong in a secret store or environment variable, never in a repository, a support ticket, a screenshot or a shared document.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\"><strong>Test on staging first.<\/strong> Do not point a new integration at a live store until you have watched it run end to end somewhere disposable. Several endpoints write data that cannot be undone through the API \u2014 accepting a registration creates a coupon, and on a store with automatic payouts a payout request may pay real money immediately.<\/div><\/div>\r\n\r\n<h3 id=\"personal-data\">Personal data<\/h3>\r\n<p>Several endpoints return personal data about real people: affiliate names, logins and email addresses, registration profile fields such as phone numbers and websites, and any custom registration fields your store collects.<\/p>\r\n<ul>\r\n<li>Under the GDPR, the UK GDPR and comparable regimes, <strong>you<\/strong> are the data controller for that information. Sending it to a third-party tool generally makes that tool a processor, which usually means you need a lawful basis for the transfer and an appropriate data-processing agreement with the provider.<\/li>\r\n<li>Deliberate omissions in this API are safety features, not oversights: order rows carry no customer data, payout destination details are never returned, click IP addresses are never exposed, and logins and emails are withheld from callers who are neither an admin nor the person concerned. If you add fields back with the response filters, you take on responsibility for what you have exposed and to whom.<\/li>\r\n<li>Grant the narrowest access that does the job. A read-only key on a dedicated account, scoped to the endpoints you actually call, limits the blast radius of a leak far more effectively than anything you can add afterwards.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">This page is general guidance about how the software behaves, not legal advice. If you are unsure whether a particular integration is lawful in your jurisdiction, take proper advice before building it.<\/div><\/div>\r\n\r\n<h3 id=\"money-and-financial-records\">Money and financial records<\/h3>\r\n<p>Commission figures, payout amounts and report totals come from the plugin's own records and are provided for information. They are <strong>not<\/strong> an accounting system, a tax record or a substitute for your payment provider's statements.<\/p>\r\n<ul>\r\n<li>Reconcile against your gateway and your books before paying anybody or filing anything. Figures can legitimately change \u2014 refunds, cancellations, manual adjustments and snapshot rebuilds all move them.<\/li>\r\n<li>All-time statistics are read from stored snapshots that can lag reality in either direction. Where the number matters, check <code>source<\/code> and <code>last_refreshed<\/code>, or request a fresh calculation.<\/li>\r\n<li>The plugin's authors accept no responsibility for payments made, withheld or duplicated as a result of an integration you build.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"treat-inbound-webhooks-as-untrusted\">Treat inbound webhooks as untrusted<\/h3>\r\n<p>A webhook receiver is a public HTTP endpoint that anybody on the internet can send a request to. Nothing about a payload proves it came from your store <em>except<\/em> a valid signature.<\/p>\r\n<ul>\r\n<li>Always verify the HMAC signature and reject stale timestamps before acting on a delivery. If your platform cannot verify signatures, do not let it take consequential action on a payload alone \u2014 read the object back from the API first.<\/li>\r\n<li>Deliveries are at-least-once and unordered. Build handlers that tolerate duplicates and out-of-sequence arrivals.<\/li>\r\n<li>Validate and escape payload values like any other external input before writing them into your own systems.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"third-party-and-ai-services\">Third-party and AI services<\/h3>\r\n<p>Connecting an external platform or an AI assistant means transmitting affiliate data to that provider, where it may be logged, retained or used for their own purposes according to their terms \u2014 not yours. Check what you are agreeing to before you connect it.<\/p>\r\n<p>AI agents carry a specific extra risk: an agent acts on text it reads, and some of that text can come from outside your control \u2014 an application's \"how will you promote us\" field, a campaign name, a website URL. An agent with write access can be steered by content like that into taking actions you never asked for. Give agents read-only keys unless you have a concrete reason not to, and never give one the <code>manage<\/code> scope.<\/p>\r\n\r\n<h3 id=\"stability-and-support\">Stability and support<\/h3>\r\n<ul>\r\n<li>Endpoints, response fields and defaults may change between plugin versions. Fields may be added at any time, so write clients that ignore unfamiliar keys rather than failing on them.<\/li>\r\n<li>Do not depend on anything not documented here \u2014 internal option names, database tables, undocumented fields or the exact wording of a message. The <code>code<\/code> in an error response is the stable part; the <code>message<\/code> is not, and is translated.<\/li>\r\n<li>Rate limits, cache windows and throttles exist to protect your site and may be adjusted in future releases. Handle <code>429<\/code> gracefully rather than assuming a fixed budget.<\/li>\r\n<li>Support covers the plugin and its own endpoints. Debugging custom integrations, third-party platforms and AI tooling is outside its scope, though the <code>\/me<\/code>, <code>\/openapi<\/code> and <code>\/webhooks<\/code> endpoints are usually enough to work out where a problem lies.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"no-warranty\">No warranty<\/h3>\r\n<p>Coupon Affiliates is free software, licensed under the <strong>GNU General Public License version 3<\/strong>. Sections 15 and 16 of that license disclaim all warranties and limit liability, and those terms apply to the API exactly as they apply to the rest of the plugin. The API, the code samples in these docs and the integration patterns they describe are therefore provided <strong>as is<\/strong>, without warranty of any kind.<\/p>\r\n<p>The examples are illustrative starting points, not production-hardened code: they omit the logging, retry policy, input validation and secret management your own environment will need. Review, adapt and test anything you take from here before relying on it.<\/p>\r\n<p>To the fullest extent permitted by law, the plugin's authors accept no liability for loss or damage arising from use of the API \u2014 including lost or exposed data, incorrect commission or payout amounts, missed or duplicated webhook deliveries, or the actions of any third-party service or automated agent you connect to it. Nothing here affects any statutory rights that cannot lawfully be excluded.<\/p>\r\n\r\n<h3 id=\"licensing\">Licensing<\/h3>\r\n<p>The plugin, its API and its add-ons are distributed under the <a href=\"https:\/\/www.gnu.org\/licenses\/gpl-3.0.html\" target=\"_blank\" rel=\"noopener\">GPLv3<\/a>; a copy ships as <code>license.txt<\/code> in the plugin folder. You are free to use, study, modify and redistribute the code on those terms.<\/p>\r\n<ul>\r\n<li><strong>Nothing on this page restricts those rights.<\/strong> It is operational guidance and a disclaimer of warranty and liability \u2014 which the GPL expressly permits \u2014 not an additional condition on using the software.<\/li>\r\n<li><strong>The code samples in these docs are yours to use.<\/strong> Copy, adapt and ship them in your own integrations, commercial or otherwise, with no attribution required. They are short illustrative snippets, published so that people can build against the API.<\/li>\r\n<li><strong>Your integration is your own work.<\/strong> A client that talks to this API over HTTP is a separate program; calling a REST endpoint does not make your codebase a derivative of the plugin. If you instead modify or bundle the plugin's own PHP, the GPL applies to what you distribute in the ordinary way.<\/li>\r\n<li><strong>The licence covers the software, not your obligations.<\/strong> It says nothing about data protection, consumer law or your duties to your own affiliates and customers \u2014 those apply regardless of how the plugin is licensed.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">Two habits prevent most serious incidents: give every integration its own key with the narrowest scope that works, and keep a current backup before running anything that writes in bulk.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=performance-caching\" data-rad-page=\"performance-caching\"><span>Previous<\/span>Performance &amp; Caching<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=endpoint-index\" data-rad-page=\"endpoint-index\"><span>Next<\/span>Endpoint Index<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-endpoint-index\" data-rad-page=\"endpoint-index\" data-rad-title=\"Endpoint Index\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Getting Started<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Endpoint Index<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Every route in the API, with the access level and scope it needs. All paths are relative to <code>https:\/\/example.com\/wp-json\/wcusage\/v2<\/code>.<\/p>\r\n<table><thead><tr><th>Method &amp; path<\/th><th>Access<\/th><th>Scope<\/th><th>Purpose<\/th><\/tr><\/thead><tbody>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/me<\/code><\/td><td>Any logged-in user<\/td><td>\u2014<\/td><td>Identify the caller, its access level and scopes.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/affiliates<\/code><\/td><td>Admin<\/td><td>read<\/td><td>List affiliates with coupons and balances.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/affiliates\/{id}<\/code><\/td><td>Admin or self<\/td><td>read<\/td><td>One affiliate, with profile fields and groups.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/affiliates\/{id}\/stats<\/code><\/td><td>Admin or self<\/td><td>read<\/td><td>Totals across all of an affiliate's coupons.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/coupons<\/code><\/td><td>Admin<\/td><td>read<\/td><td>List affiliate coupons.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/coupons\/{id}<\/code><\/td><td>Admin, owner, upline<\/td><td>read<\/td><td>One coupon, with commission rates and referral URL.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/coupons\/{id}\/stats<\/code><\/td><td>Admin, owner, upline<\/td><td>read (+write to refresh)<\/td><td>Sales and commission, all-time or by date range.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/coupons\/{id}\/orders<\/code><\/td><td>Admin, owner, upline<\/td><td>read<\/td><td>Orders referred by a coupon, with commission per order.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/payouts<\/code><\/td><td>Any logged-in user<\/td><td>read<\/td><td>List payouts. Non-admins see only their own.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-post\">POST<\/span> <code>\/payouts<\/code><\/td><td>Admin or coupon owner<\/td><td>write<\/td><td>Request a payout for a coupon's unpaid balance.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/payouts\/{id}<\/code><\/td><td>Admin or owner<\/td><td>read<\/td><td>One payout.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-post\">POST<\/span> <code>\/payouts\/{id}\/status<\/code><\/td><td>Admin<\/td><td>write<\/td><td>Change a payout status. Bookkeeping only.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/registrations<\/code><\/td><td>Admin<\/td><td>read<\/td><td>List affiliate applications.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/registrations\/{id}<\/code><\/td><td>Admin<\/td><td>read<\/td><td>One application.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-post\">POST<\/span> <code>\/registrations\/{id}\/status<\/code><\/td><td>Admin<\/td><td>write<\/td><td>Approve or decline an application.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/clicks\/stats<\/code><\/td><td>Admin, or owner with <code>coupon_id<\/code><\/td><td>read<\/td><td>Clicks, conversions and conversion rate.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/events<\/code><\/td><td>Admin<\/td><td>read<\/td><td>Change feed with a cursor for polling.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/reports\/summary<\/code><\/td><td>Admin<\/td><td>read<\/td><td>Store-wide totals and top affiliates.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/keys<\/code><\/td><td>Admin<\/td><td>manage<\/td><td>List API keys.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-post\">POST<\/span> <code>\/keys<\/code><\/td><td>Admin<\/td><td>manage<\/td><td>Create an API key.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-delete\">DELETE<\/span> <code>\/keys\/{id}<\/code><\/td><td>Admin<\/td><td>manage<\/td><td>Revoke an API key.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/webhooks<\/code><\/td><td>Admin<\/td><td>manage<\/td><td><strong>PRO.<\/strong> List webhook endpoints.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-post\">POST<\/span> <code>\/webhooks<\/code><\/td><td>Admin<\/td><td>manage<\/td><td><strong>PRO.<\/strong> Create a webhook endpoint.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-patch\">PATCH<\/span> <code>\/webhooks\/{id}<\/code><\/td><td>Admin<\/td><td>manage<\/td><td><strong>PRO.<\/strong> Change status or subscribed events.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-delete\">DELETE<\/span> <code>\/webhooks\/{id}<\/code><\/td><td>Admin<\/td><td>manage<\/td><td><strong>PRO.<\/strong> Delete a webhook endpoint.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-post\">POST<\/span> <code>\/webhooks\/{id}\/test<\/code><\/td><td>Admin<\/td><td>manage<\/td><td><strong>PRO.<\/strong> Send a test delivery.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/webhooks\/events<\/code><\/td><td>Admin<\/td><td>read<\/td><td><strong>PRO.<\/strong> The catalog of subscribable events.<\/td><\/tr>\r\n<tr><td><span class=\"rad-method rad-get\">GET<\/span> <code>\/openapi<\/code><\/td><td>Public by default<\/td><td>\u2014<\/td><td>Machine-readable description of this API.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>\"Owner\" means the affiliate the coupon is assigned to; \"upline\" means their multi-level parent, which exists in PRO only. Payout and webhook routes are PRO only. Every endpoint can additionally be switched off per site \u2014 see <a href=\"#\">Enabling the API<\/a>.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=security-disclaimers\" data-rad-page=\"security-disclaimers\"><span>Previous<\/span>Security &amp; Disclaimers<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=me\" data-rad-page=\"me\"><span>Next<\/span>Me<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-me\" data-rad-page=\"me\" data-rad-title=\"Me\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Me<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Identify the authenticated caller, its access level and its scopes. This is the ideal first call for any integration \u2014 an AI agent can use it to discover what it is allowed to do before attempting anything.<\/p>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/me<\/code><\/p>\r\n<p><strong>Permission:<\/strong> any authenticated user. No scope is needed for the identity fields; the <code>coupons<\/code> array requires <code>read<\/code>.<\/p>\r\n<pre><code>{\r\n  \"user\": { \"id\": 1456, \"display_name\": \"Sarah J\", \"login\": \"sarahj\", \"email\": \"sarah@example.com\" },\r\n  \"is_admin\": false,\r\n  \"is_affiliate\": true,\r\n  \"auth\": { \"method\": \"api_key\", \"key_id\": 3, \"scopes\": [\"read\"] },\r\n  \"coupons\": [\r\n    {\r\n      \"id\": 8338,\r\n      \"code\": \"sarah10\",\r\n      \"user_id\": 1456,\r\n      \"date_created\": \"2023-05-02T10:11:12\",\r\n      \"unpaid_commission\": 40.46,\r\n      \"pending_order_commission\": 0,\r\n      \"pending_payout_commission\": 0\r\n    }\r\n  ],\r\n  \"api_version\": \"8.2.0\"\r\n}<\/code><\/pre>\r\n<table><thead><tr><th>Field<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>user<\/td><td>object<\/td><td><code>id<\/code> and <code>display_name<\/code> always; <code>login<\/code> and <code>email<\/code> only when the caller is an admin or is that user.<\/td><\/tr>\r\n<tr><td>is_admin<\/td><td>boolean<\/td><td>Whether the caller passes the plugin's admin access check.<\/td><\/tr>\r\n<tr><td>is_affiliate<\/td><td>boolean<\/td><td>Whether the user has at least one affiliate coupon.<\/td><\/tr>\r\n<tr><td>auth.method<\/td><td>string<\/td><td><code>api_key<\/code> or <code>wordpress<\/code>.<\/td><\/tr>\r\n<tr><td>auth.key_id<\/td><td>integer<\/td><td>The API key's ID, or <code>null<\/code>.<\/td><\/tr>\r\n<tr><td>auth.scopes<\/td><td>array<\/td><td>Scopes in force, or <code>null<\/code> for capability-based auth (full access for that user).<\/td><\/tr>\r\n<tr><td>coupons<\/td><td>array<\/td><td>The caller's own affiliate coupons with balances. Empty for non-affiliates.<\/td><\/tr>\r\n<tr><td>api_version<\/td><td>string<\/td><td>The installed Coupon Affiliates version.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\"><code>auth.scopes: null<\/code> means the request is <em>not<\/em> scope-limited \u2014 it authenticated with an application password or cookie, so the user's capabilities are the only limit.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=endpoint-index\" data-rad-page=\"endpoint-index\"><span>Previous<\/span>Endpoint Index<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=affiliates\" data-rad-page=\"affiliates\"><span>Next<\/span>Affiliates<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-affiliates\" data-rad-page=\"affiliates\" data-rad-title=\"Affiliates\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Affiliates<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>An affiliate is a WordPress user with at least one published coupon assigned to them. There is no separate affiliate table; these endpoints derive the entity the same way the admin list table does, and aggregate across all of an affiliate's coupons.<\/p>\r\n\r\n<h3 id=\"list-affiliates\">List affiliates<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/affiliates<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>search<\/td><td>string<\/td><td>Partial match against user login, email or display name.<\/td><\/tr>\r\n<tr><td>page \/ per_page<\/td><td>integer<\/td><td>Standard pagination.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>[\r\n  {\r\n    \"user\": { \"id\": 1456, \"display_name\": \"Sarah J\", \"login\": \"sarahj\", \"email\": \"sarah@example.com\" },\r\n    \"coupons\": [\r\n      { \"id\": 8338, \"code\": \"sarah10\", \"user_id\": 1456, \"date_created\": \"2023-05-02T10:11:12\",\r\n        \"unpaid_commission\": 40.46, \"pending_order_commission\": 0, \"pending_payout_commission\": 0 }\r\n    ],\r\n    \"unpaid_commission\": 40.46,\r\n    \"pending_payout_commission\": 0\r\n  }\r\n]<\/code><\/pre>\r\n<p>Results are ordered by user ID ascending. Only users holding at least one <strong>published<\/strong> assigned coupon appear.<\/p>\r\n\r\n<h3 id=\"get-one-affiliate\">Get one affiliate<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/affiliates\/{user_id}<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, or the affiliate themselves. <code>read<\/code> scope.<\/p>\r\n<p>Returns the list shape plus detail fields, and each coupon carries its cached all-time <code>stats<\/code> block:<\/p>\r\n<table><thead><tr><th>Extra field<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>date_registered<\/td><td>string<\/td><td>When the WordPress account was created.<\/td><\/tr>\r\n<tr><td>profile<\/td><td>object<\/td><td>Registration profile fields: <code>phone<\/code>, <code>website<\/code>, <code>promote<\/code>, <code>referrer<\/code>.<\/td><\/tr>\r\n<tr><td>groups<\/td><td>array<\/td><td>Affiliate group roles the user holds.<\/td><\/tr>\r\n<tr><td>mla_parents<\/td><td>object<\/td><td><strong>PRO only.<\/strong> Multi-level upline chain, keyed by tier. Absent entirely in the free build.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n\r\n<h3 id=\"affiliate-stats\">Affiliate stats<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/affiliates\/{user_id}\/stats<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, or the affiliate themselves. <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>from<\/td><td>date<\/td><td>Optional start date (<code>Y-m-d<\/code>). When set, figures are recalculated from the orders for the range instead of read from the all-time cache.<\/td><\/tr>\r\n<tr><td>to<\/td><td>date<\/td><td>Optional end date. Defaults to today when <code>from<\/code> is set.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>{\r\n  \"user_id\": 1456,\r\n  \"from\": null,\r\n  \"to\": null,\r\n  \"totals\": {\r\n    \"orders_count\": 19,\r\n    \"total_sales\": 1977.80,\r\n    \"total_discount\": 197.78,\r\n    \"total_commission\": 181.60,\r\n    \"unpaid_commission\": 40.46,\r\n    \"pending_payout_commission\": 0\r\n  },\r\n  \"coupons\": [\r\n    { \"id\": 8338, \"code\": \"sarah10\", \"orders_count\": 19, \"total_sales\": 1977.80,\r\n      \"total_discount\": 197.78, \"total_commission\": 181.60 }\r\n  ]\r\n}<\/code><\/pre>\r\n<p>The two balance figures in <code>totals<\/code> are always current balances; they are not affected by <code>from<\/code>\/<code>to<\/code>.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">A ranged request recalculates every one of the affiliate's coupons from the order history, so one call costs as much as their whole trading history. Results are cached for 60 seconds per range, and an uncached range is limited to one per affiliate per minute \u2014 over that, the response is <code>429 wcusage_api_throttled<\/code>.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=me\" data-rad-page=\"me\"><span>Previous<\/span>Me<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=coupons\" data-rad-page=\"coupons\"><span>Next<\/span>Coupons<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-coupons\" data-rad-page=\"coupons\" data-rad-title=\"Coupons\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Coupons<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Affiliate coupons, with balances, stats and referred orders. Reads go through the same functions the affiliate dashboard uses, so caps, rounding and commission rules always match what affiliates see.<\/p>\r\n\r\n<h3 id=\"list-coupons\">List coupons<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/coupons<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>user_id<\/td><td>integer<\/td><td>Only coupons assigned to this affiliate. Omit for every assigned affiliate coupon.<\/td><\/tr>\r\n<tr><td>search<\/td><td>string<\/td><td>Match against the coupon code.<\/td><\/tr>\r\n<tr><td>page \/ per_page<\/td><td>integer<\/td><td>Standard pagination.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Returns published coupons that have an affiliate assigned, newest first. Coupons with no assigned affiliate are never listed.<\/p>\r\n\r\n<h3 id=\"get-one-coupon\">Get one coupon<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/coupons\/{id}<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, the assigned affiliate, or their multi-level upline (PRO). <code>read<\/code> scope.<\/p>\r\n<pre><code>{\r\n  \"id\": 8338,\r\n  \"code\": \"sarah10\",\r\n  \"user_id\": 1456,\r\n  \"user\": { \"id\": 1456, \"display_name\": \"Sarah J\" },\r\n  \"date_created\": \"2023-05-02T10:11:12\",\r\n  \"unpaid_commission\": 40.46,\r\n  \"pending_order_commission\": 0,\r\n  \"pending_payout_commission\": 0,\r\n  \"stats\": {\r\n    \"orders_count\": 19, \"total_sales\": 1977.80, \"total_discount\": 197.78,\r\n    \"total_shipping\": 0, \"total_commission\": 181.60, \"last_refreshed\": \"2026-08-01T02:00:00\"\r\n  },\r\n  \"commission\": { \"percent\": 10, \"percent_override\": \"\", \"fixed_per_order\": \"\", \"fixed_per_product\": \"\" },\r\n  \"referral_url\": \"https:\/\/example.com\/affiliate-dashboard\/?couponid=sarah10\"\r\n}<\/code><\/pre>\r\n<table><thead><tr><th>Field<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>unpaid_commission<\/td><td>Commission earned, cleared, and not yet paid out or requested.<\/td><\/tr>\r\n<tr><td>pending_order_commission<\/td><td>Commission on orders still inside the pending period (not yet payable).<\/td><\/tr>\r\n<tr><td>pending_payout_commission<\/td><td>Commission tied up in payouts that have been requested but not paid.<\/td><\/tr>\r\n<tr><td>commission.percent<\/td><td>The percentage rate resolved for this coupon: its own override if it has one, otherwise the store's default rate.<\/td><\/tr>\r\n<tr><td>commission.percent_override<\/td><td>The per-coupon override itself. Empty when the coupon inherits the store default.<\/td><\/tr>\r\n<tr><td>commission.fixed_per_order<\/td><td>Fixed amount per referred order, if configured. Empty when unused.<\/td><\/tr>\r\n<tr><td>commission.fixed_per_product<\/td><td>Fixed amount per product, if configured. Empty when unused.<\/td><\/tr>\r\n<tr><td>referral_url<\/td><td>The affiliate's referral URL for this coupon, pointing at the affiliate dashboard page.<\/td><\/tr>\r\n<tr><td>stats.last_refreshed<\/td><td>When the stored snapshot was last rebuilt, or <code>null<\/code> if it never has been.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Draft and private coupons are addressable \u2014 a coupon does not have to be published to have an affiliate and a balance. Trashed and auto-draft coupons answer <code>404<\/code>.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Always reference coupons by <strong>ID<\/strong>. WooCommerce coupon codes are case-insensitive and not guaranteed unique, and the stats layer is keyed by code \u2014 see <code>code_ambiguous<\/code> below.<\/div><\/div>\r\n\r\n<h3 id=\"coupon-stats\">Coupon stats<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/coupons\/{id}\/stats<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, owner, or MLA upline. <code>read<\/code> scope; <code>refresh=true<\/code> additionally needs <code>write<\/code>.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>from \/ to<\/td><td>date<\/td><td>Optional range. Without dates, the stored all-time snapshot is returned (fast). With dates, the range is calculated from the orders.<\/td><\/tr>\r\n<tr><td>refresh<\/td><td>boolean<\/td><td>Recalculate the all-time figures from the orders and save the result. Default <code>false<\/code>. Ignored when a date range is given, since those are always calculated.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>{\r\n  \"coupon_id\": 8338,\r\n  \"code\": \"sarah10\",\r\n  \"from\": null,\r\n  \"to\": null,\r\n  \"source\": \"cache\",\r\n  \"code_ambiguous\": false,\r\n  \"orders_count\": 19,\r\n  \"total_sales\": 1977.80,\r\n  \"total_discount\": 197.78,\r\n  \"total_shipping\": 0,\r\n  \"total_commission\": 181.60,\r\n  \"status_counts\": { \"Completed\": 17, \"Refunded\": 2 },\r\n  \"last_refreshed\": \"2026-08-01T02:00:00\",\r\n  \"unpaid_commission\": 40.46,\r\n  \"pending_order_commission\": 0,\r\n  \"pending_payout_commission\": 0\r\n}<\/code><\/pre>\r\n<table><thead><tr><th>Field<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>source<\/td><td><code>cache<\/code> \u2014 the stored snapshot or a short-lived cached calculation. <code>live<\/code> \u2014 freshly calculated for this request. <code>throttled<\/code> \u2014 a rebuild was wanted but the per-coupon budget was spent, so the stored snapshot was returned instead.<\/td><\/tr>\r\n<tr><td>code_ambiguous<\/td><td><code>true<\/code> when another published coupon answers to the same code. The stats layer is keyed by code, so figures for this coupon are not reliably addressable and are never saved back to it.<\/td><\/tr>\r\n<tr><td>status_counts<\/td><td>Order counts by display status name, e.g. <code>{\"Completed\": 17}<\/code>. An empty object when the figures came from the stored snapshot, which holds no breakdown.<\/td><\/tr>\r\n<tr><td>last_refreshed<\/td><td><code>null<\/code> for ranged requests \u2014 those are always calculated, so there is no \"last refreshed\" moment to report.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>The response shape is stable regardless of which path answered: <code>status_counts<\/code> and <code>last_refreshed<\/code> are always present, even when empty.<\/p>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\"><code>refresh=true<\/code> is a write wearing a GET's clothes \u2014 it rescans the coupon's entire order history and saves the result. A read-only key gets <code>403 wcusage_api_insufficient_scope<\/code>; it still receives figures from the stored snapshot on an ordinary request.<\/div><\/div>\r\n\r\n<h3 id=\"referred-orders\">Referred orders<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/coupons\/{id}\/orders<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, owner, or MLA upline. <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>from \/ to<\/td><td>date<\/td><td>Optional date range.<\/td><\/tr>\r\n<tr><td>status<\/td><td>string<\/td><td>Order status slug without the <code>wc-<\/code> prefix, e.g. <code>completed<\/code>.<\/td><\/tr>\r\n<tr><td>page \/ per_page<\/td><td>integer<\/td><td>Standard pagination.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>[\r\n  {\r\n    \"order_id\": 8355,\r\n    \"date\": \"2023-07-28T12:46:58\",\r\n    \"status\": \"completed\",\r\n    \"total\": 179.80,\r\n    \"discount\": 17.98,\r\n    \"commission\": 16.18\r\n  }\r\n]<\/code><\/pre>\r\n<p>Newest first. Only orders that actually counted towards <em>this<\/em> coupon are listed \u2014 an order that used the code but was later reassigned to another affiliate is excluded, which keeps this endpoint consistent with <code>\/stats<\/code>.<\/p>\r\n<p>One request considers at most <strong>5,000<\/strong> orders (the newest ones). When that cap is hit, the response carries the header <code>X-WCUsage-Truncated: 1<\/code>, so a client can tell a capped total from a real one. Raise it with the <code>wcusage_api_max_order_rows<\/code> filter if your program needs a deeper window.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Order rows deliberately carry <strong>no customer data<\/strong> \u2014 only IDs, totals and commission. A trusted server-side integration can add fields with the <code>wcusage_api_order_item<\/code> filter.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=affiliates\" data-rad-page=\"affiliates\"><span>Previous<\/span>Affiliates<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=payouts\" data-rad-page=\"payouts\"><span>Next<\/span>Payouts<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-payouts\" data-rad-page=\"payouts\" data-rad-title=\"Payouts\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Payouts<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Read payout history and create payout requests. Payouts are a <strong>PRO<\/strong> feature; on a build or install without them, these endpoints are absent or answer <code>501 wcusage_api_unavailable<\/code>.<\/p>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">Changing a payout's <strong>status<\/strong> is bookkeeping only \u2014 no payment gateway is ever contacted. <strong>Creating<\/strong> a payout is different: if your store has automatic payouts enabled, the normal submit flow may pay it immediately through PayPal, Stripe or Wise, exactly as a dashboard request would. The response flags this with <code>gateway_triggered<\/code>.<\/div><\/div>\r\n<p>These are the highest-consequence endpoints in the API. Prove any automation on a staging store before pointing it at a live one, reconcile amounts against your payment provider rather than treating these figures as an accounting record, and keep the responsibility for verifying payments with a person. If you would rather nothing automated could ever create a payout, switch the <code>\/payouts<\/code> endpoints off on the API screen \u2014 everything else keeps working.<\/p>\r\n\r\n<h3 id=\"list-payouts\">List payouts<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/payouts<\/code><\/p>\r\n<p><strong>Permission:<\/strong> any authenticated user, <code>read<\/code> scope. Non-admins are always restricted to their own payouts, whatever <code>user_id<\/code> they send.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>user_id<\/td><td>integer<\/td><td>Filter by affiliate. Admin only \u2014 overridden for everyone else.<\/td><\/tr>\r\n<tr><td>coupon_id<\/td><td>integer<\/td><td>Filter by coupon.<\/td><\/tr>\r\n<tr><td>status<\/td><td>string<\/td><td>One of <code>pending<\/code>, <code>created<\/code>, <code>paid<\/code>, <code>cancel<\/code>.<\/td><\/tr>\r\n<tr><td>from \/ to<\/td><td>date<\/td><td>Filter by request date.<\/td><\/tr>\r\n<tr><td>page \/ per_page<\/td><td>integer<\/td><td>Standard pagination.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>{\r\n  \"id\": 321,\r\n  \"user\": { \"id\": 1456, \"display_name\": \"Sarah J\" },\r\n  \"coupon_id\": 8338,\r\n  \"coupon_code\": \"sarah10\",\r\n  \"amount\": 40.46,\r\n  \"method\": \"PayPal\",\r\n  \"method_type\": \"paypal\",\r\n  \"status\": \"pending\",\r\n  \"has_details\": true,\r\n  \"transaction_id\": \"\",\r\n  \"invoice_id\": 0,\r\n  \"date\": \"2026-08-07T09:00:00\",\r\n  \"date_paid\": null\r\n}<\/code><\/pre>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Payout destination details \u2014 PayPal addresses, bank accounts, wallet addresses \u2014 are <strong>never<\/strong> returned by the API. Only the boolean <code>has_details<\/code> tells you whether any are stored.<\/div><\/div>\r\n\r\n<h3 id=\"get-one-payout\">Get one payout<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/payouts\/{id}<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin or the payout's owner. <code>read<\/code> scope. Somebody else's payout answers <code>404<\/code>.<\/p>\r\n\r\n<h3 id=\"request-a-payout\">Request a payout<\/h3>\r\n<p><span class=\"rad-method rad-post\">POST<\/span> <code>\/wp-json\/wcusage\/v2\/payouts<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin (any coupon), or the coupon's affiliate (their own). <code>write<\/code> scope.<\/p>\r\n<table><thead><tr><th>Body param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>coupon_id<\/td><td>integer<\/td><td>Required. The coupon's <strong>full unpaid balance<\/strong> is requested; there is no partial-amount parameter.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>curl -X POST https:\/\/example.com\/wp-json\/wcusage\/v2\/payouts \\\r\n  -H \"Authorization: Bearer wcus_...\" \\\r\n  -H \"Content-Type: application\/json\" \\\r\n  -d '{\"coupon_id\": 8338}'<\/code><\/pre>\r\n<p>Returns <code>201<\/code> with the created payout, plus two extra fields:<\/p>\r\n<ul>\r\n<li><code>gateway_triggered<\/code> \u2014 <code>true<\/code> when auto-accept moved the payout straight to <code>created<\/code> or <code>paid<\/code>, meaning a real gateway may have been called.<\/li>\r\n<li><code>gateway_notice<\/code> \u2014 present only when a gateway handler produced a message worth passing on.<\/li>\r\n<\/ul>\r\n\r\n<h4>Idempotency<\/h4>\r\n<p>Only one open request can exist per coupon at a time. If one already exists (<code>pending<\/code>, <code>created<\/code> or <code>processing<\/code>), the existing payout is returned with <code>200<\/code> and the header <code>X-WCUsage-Existing: 1<\/code> \u2014 so a retrying client can never create duplicates. Concurrent requests for the same coupon are serialised with a lock; the loser gets <code>409 wcusage_api_in_progress<\/code>.<\/p>\r\n\r\n<h4>Rules that are enforced<\/h4>\r\n<p>Requests are held to the same rules as the affiliate dashboard, so the API cannot be used to route around a store's configuration:<\/p>\r\n<table><thead><tr><th>Error code<\/th><th>Condition<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>payouts_disabled<\/code><\/td><td>Payouts are switched off in the plugin settings.<\/td><\/tr>\r\n<tr><td><code>requests_disabled<\/code><\/td><td>Affiliate self-service requests are switched off; only the store owner creates payouts. Not applied to admin callers.<\/td><\/tr>\r\n<tr><td><code>no_affiliate<\/code><\/td><td>The coupon has no affiliate assigned.<\/td><\/tr>\r\n<tr><td><code>no_balance<\/code><\/td><td>The unpaid balance is zero or less.<\/td><\/tr>\r\n<tr><td><code>below_threshold<\/code><\/td><td>The unpaid balance is under the store's minimum payout threshold. Enforced for admins too.<\/td><\/tr>\r\n<tr><td><code>no_payout_details<\/code><\/td><td>The affiliate has not saved payout details and the store requires them.<\/td><\/tr>\r\n<tr><td><code>method_disabled<\/code><\/td><td>The affiliate's saved payout method is no longer enabled on the store. Not applied to admin callers.<\/td><\/tr>\r\n<tr><td><code>invoice_required<\/code><\/td><td>The store requires an invoice upload for this payout method. Invoices cannot be uploaded through the API, so the request must be made from the affiliate dashboard.<\/td><\/tr>\r\n<tr><td><code>not_created<\/code><\/td><td>Everything checked out but the site refused the request \u2014 typically a custom <code>wcusage_before_payout_submit<\/code> filter, or a failed write.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n\r\n<h3 id=\"update-payout-status\">Update payout status<\/h3>\r\n<p><span class=\"rad-method rad-post\">POST<\/span> <code>\/wp-json\/wcusage\/v2\/payouts\/{id}\/status<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>write<\/code> scope.<\/p>\r\n<table><thead><tr><th>Body param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>status<\/td><td>string<\/td><td>Required. One of <code>pending<\/code>, <code>created<\/code>, <code>paid<\/code>, <code>cancel<\/code>.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>This runs the same status flow as the admin screen: balances move between the unpaid, pending-payout and paid pools, the activity log records the change, notification emails are sent and webhooks fire. <strong>No payment gateway is contacted.<\/strong><\/p>\r\n\r\n<h4>Permitted transitions<\/h4>\r\n<table><thead><tr><th>From<\/th><th>To<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>pending<\/code><\/td><td><code>paid<\/code>, <code>cancel<\/code>, <code>created<\/code><\/td><\/tr>\r\n<tr><td><code>created<\/code><\/td><td><code>paid<\/code>, <code>cancel<\/code>, <code>pending<\/code><\/td><\/tr>\r\n<tr><td><code>paid<\/code><\/td><td><code>cancel<\/code>, <code>pending<\/code>, <code>created<\/code><\/td><\/tr>\r\n<tr><td><code>cancel<\/code><\/td><td><code>pending<\/code>, <code>created<\/code><\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Anything else is refused with <code>400 wcusage_api_invalid_transition<\/code> rather than leaving the balances inconsistent. Setting the status a payout already has returns <code>400 wcusage_api_no_change<\/code>.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\"><code>processing<\/code> and <code>failed<\/code> are deliberately not accepted. Nothing in the plugin writes them and the status arithmetic has no branch for them, so moving a payout through either one would strand its balance with no way back.<\/div><\/div>\r\n<p>Two further guards:<\/p>\r\n<ul>\r\n<li><strong>Re-opening a cancelled payout<\/strong> takes its amount back out of the unpaid balance. If that money is no longer there \u2014 already paid out, or the balance was edited \u2014 the request is refused with <code>409 wcusage_api_insufficient_unpaid<\/code> rather than letting the ledger drift.<\/li>\r\n<li><strong>Concurrent status changes<\/strong> are settled by a conditional update. Only the request that actually changes the row runs the balance arithmetic; the loser gets <code>409 wcusage_api_conflict<\/code>.<\/li>\r\n<\/ul>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=coupons\" data-rad-page=\"coupons\"><span>Previous<\/span>Coupons<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=registrations\" data-rad-page=\"registrations\"><span>Next<\/span>Registrations<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-registrations\" data-rad-page=\"registrations\" data-rad-title=\"Registrations\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Registrations<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Read and moderate affiliate applications. These endpoints read the plugin's registration table, and approval goes through the same function the admin screen calls, so the full accept flow runs identically.<\/p>\r\n\r\n<h3 id=\"list-registrations\">List registrations<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/registrations<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>status<\/td><td>string<\/td><td><code>pending<\/code>, <code>accepted<\/code> or <code>declined<\/code>.<\/td><\/tr>\r\n<tr><td>user_id<\/td><td>integer<\/td><td>Filter by WordPress user.<\/td><\/tr>\r\n<tr><td>page \/ per_page<\/td><td>integer<\/td><td>Standard pagination.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>{\r\n  \"id\": 512,\r\n  \"user\": { \"id\": 2201, \"display_name\": \"New Affiliate\", \"login\": \"newaffiliate\", \"email\": \"new@example.com\" },\r\n  \"coupon_code\": \"newaff15\",\r\n  \"status\": \"pending\",\r\n  \"type\": \"\",\r\n  \"promote\": \"Instagram and my newsletter\",\r\n  \"referrer\": \"\",\r\n  \"website\": \"https:\/\/blog.example.net\",\r\n  \"custom_fields\": { \"Audience size\": \"12000\" },\r\n  \"date\": \"2026-08-05T18:22:41\",\r\n  \"date_accepted\": null\r\n}<\/code><\/pre>\r\n<p><code>custom_fields<\/code> holds whatever extra registration fields the store has configured, as a flat object. It is an empty object when there are none.<\/p>\r\n\r\n<h3 id=\"get-one-registration\">Get one registration<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/registrations\/{id}<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>read<\/code> scope.<\/p>\r\n\r\n<h3 id=\"approve-or-decline\">Approve or decline<\/h3>\r\n<p><span class=\"rad-method rad-post\">POST<\/span> <code>\/wp-json\/wcusage\/v2\/registrations\/{id}\/status<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>write<\/code> scope.<\/p>\r\n<table><thead><tr><th>Body param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>status<\/td><td>string<\/td><td>Required. <code>accepted<\/code> or <code>declined<\/code>.<\/td><\/tr>\r\n<tr><td>message<\/td><td>string<\/td><td>Optional message included in the notification email.<\/td><\/tr>\r\n<tr><td>send_email<\/td><td>boolean<\/td><td>Whether to send the notification email. Default <code>true<\/code>.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Returns the updated registration.<\/p>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">Accepting runs the <strong>full approval flow<\/strong>: the affiliate coupon is created from the template, roles are assigned, emails are sent and hooks fire \u2014 exactly as if approved from the admin screen.<\/div><\/div>\r\n<table><thead><tr><th>Error code<\/th><th>Condition<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>no_change<\/code> (400)<\/td><td>The registration already has that status.<\/td><\/tr>\r\n<tr><td><code>already_accepted<\/code> (400)<\/td><td>The registration was already accepted. Accepting is not reversible through the API, because an accept \u2192 decline \u2192 accept cycle would create a second published coupon with the same code and its own separate balance. Reverse it from the admin screens if you really must.<\/td><\/tr>\r\n<tr><td><code>no_coupon_code<\/code> (409)<\/td><td>The registration carries no coupon code, so its status cannot be changed. Add one from the admin screens first.<\/td><\/tr>\r\n<tr><td><code>not_updated<\/code> (409)<\/td><td>Something on the site refused the change. The response reports what the row actually says rather than what was asked for.<\/td><\/tr>\r\n<tr><td><code>unavailable<\/code> (501)<\/td><td>The registrations table does not exist on this install.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=payouts\" data-rad-page=\"payouts\"><span>Previous<\/span>Payouts<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=clicks\" data-rad-page=\"clicks\"><span>Next<\/span>Clicks<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-clicks\" data-rad-page=\"clicks\" data-rad-title=\"Clicks\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Clicks<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Aggregated referral-link click statistics, counted in SQL rather than loaded row by row.<\/p>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/clicks\/stats<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin for store-wide figures; affiliates must pass a <code>coupon_id<\/code> they own. <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>coupon_id<\/td><td>integer<\/td><td>Limit to one coupon. Required for non-admin users \u2014 omitting it returns <code>400 wcusage_api_coupon_required<\/code>.<\/td><\/tr>\r\n<tr><td>campaign<\/td><td>string<\/td><td>Filter by campaign name (exact match).<\/td><\/tr>\r\n<tr><td>from \/ to<\/td><td>date<\/td><td>Optional date range.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>{\r\n  \"coupon_id\": 8338,\r\n  \"campaign\": null,\r\n  \"from\": \"2026-07-01\",\r\n  \"to\": \"2026-07-31\",\r\n  \"clicks\": 412,\r\n  \"conversions\": 19,\r\n  \"conversion_rate\": 4.61\r\n}<\/code><\/pre>\r\n<p><code>conversion_rate<\/code> is a percentage rounded to two decimals, and is <code>0<\/code> when there were no clicks.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">The IP addresses stored with clicks are never exposed through the API. If the clicks table does not exist on the install, the endpoint answers <code>501 wcusage_api_unavailable<\/code>.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=registrations\" data-rad-page=\"registrations\"><span>Previous<\/span>Registrations<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=events\" data-rad-page=\"events\"><span>Next<\/span>Events<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-events\" data-rad-page=\"events\" data-rad-title=\"Events\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Events<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>A cursor-based change feed over the plugin's activity log. This is the polling counterpart to webhooks: ask \"what happened since event X\" and act on the answer. Webhooks are PRO, so on the free version this endpoint is how you keep an external system in step.<\/p>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/events<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>after<\/td><td>integer<\/td><td>Cursor. Returns only events with a higher ID, <strong>oldest first<\/strong>. Without it, newest first.<\/td><\/tr>\r\n<tr><td>event<\/td><td>string<\/td><td>Filter by event type.<\/td><\/tr>\r\n<tr><td>user_id<\/td><td>integer<\/td><td>Filter by the acting user.<\/td><\/tr>\r\n<tr><td>page \/ per_page<\/td><td>integer<\/td><td>Standard pagination. <code>page<\/code> is ignored in cursor mode.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>[\r\n  { \"id\": 9911, \"event\": \"payout_paid\", \"event_id\": 321, \"user_id\": 1, \"info\": \"40.46\", \"date\": \"2026-08-07T10:15:00\" },\r\n  { \"id\": 9912, \"event\": \"referral\", \"event_id\": 8355, \"user_id\": 0, \"info\": \"sarah10\", \"date\": \"2026-08-07T10:16:31\" }\r\n]<\/code><\/pre>\r\n<p>The response header <code>X-WCUsage-Last-Event<\/code> carries the highest event ID returned. Store it and pass it as <code>after<\/code> on the next poll:<\/p>\r\n<pre><code>GET \/wp-json\/wcusage\/v2\/events?after=9912&amp;per_page=100\r\n\u2192 X-WCUsage-Last-Event: 9987<\/code><\/pre>\r\n\r\n<h3 id=\"event-types\">Event types<\/h3>\r\n<table><thead><tr><th>Event<\/th><th><code>event_id<\/code> refers to<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>referral<\/code><\/td><td>Order<\/td><\/tr>\r\n<tr><td><code>commission_added<\/code>, <code>commission_removed<\/code><\/td><td>Coupon<\/td><\/tr>\r\n<tr><td><code>mla_commission_added<\/code>, <code>mla_commission_removed<\/code><\/td><td>Coupon (PRO)<\/td><\/tr>\r\n<tr><td><code>registration<\/code>, <code>registration_accept<\/code><\/td><td>Registration<\/td><\/tr>\r\n<tr><td><code>payout_request<\/code>, <code>payout_paid<\/code>, <code>payout_reversed<\/code>, <code>payout_cancelled<\/code><\/td><td>Payout (PRO)<\/td><\/tr>\r\n<tr><td><code>reward_earned<\/code><\/td><td>Reward (PRO)<\/td><\/tr>\r\n<tr><td><code>new_campaign<\/code><\/td><td>Campaign (PRO)<\/td><\/tr>\r\n<tr><td><code>direct_link_domain<\/code><\/td><td>Direct link (PRO)<\/td><\/tr>\r\n<tr><td><code>mla_invite<\/code><\/td><td>Invite (PRO)<\/td><\/tr>\r\n<tr><td><code>lifetime_link_edited<\/code><\/td><td>Customer (PRO)<\/td><\/tr>\r\n<tr><td><code>api_key_created<\/code>, <code>api_key_revoked<\/code><\/td><td>API key<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p><code>user_id<\/code> is the acting user, and is <code>0<\/code> for system and guest actions such as a referral from a logged-out shopper. <code>info<\/code> is free-form context whose meaning depends on the event type.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Declined registrations are <strong>not<\/strong> written to the activity log, so they never appear in this feed. Subscribe to the <code>registration.declined<\/code> webhook (PRO) if you need them.<\/div><\/div>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">The feed needs the <strong>activity log<\/strong> enabled in the plugin settings (it is by default). When it is off, this endpoint answers <code>400 wcusage_api_log_disabled<\/code>. Webhooks are unaffected \u2014 they fire from the same funnel, before the log setting is consulted.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=clicks\" data-rad-page=\"clicks\"><span>Previous<\/span>Clicks<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reports\" data-rad-page=\"reports\"><span>Next<\/span>Reports<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-reports\" data-rad-page=\"reports\" data-rad-title=\"Reports\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Reports<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>A store-wide program summary, built for dashboards and AI assistants that need the whole picture in one call.<\/p>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/reports\/summary<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>read<\/code> scope.<\/p>\r\n<table><thead><tr><th>Param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>top<\/td><td>integer<\/td><td>How many top affiliates to include, by all-time commission. 0\u201350, default <code>10<\/code>.<\/td><\/tr>\r\n<tr><td>refresh<\/td><td>boolean<\/td><td>Bypass the 5-minute report cache. Default <code>false<\/code>.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>{\r\n  \"generated\": \"2026-08-07T14:05:00\",\r\n  \"currency\": \"USD\",\r\n  \"totals\": {\r\n    \"affiliates\": 448,\r\n    \"coupons\": 512,\r\n    \"orders_count\": 10231,\r\n    \"total_sales\": 812337.20,\r\n    \"total_discount\": 79110.55,\r\n    \"total_commission\": 81233.71,\r\n    \"unpaid_commission\": 6120.44,\r\n    \"pending_payout_commission\": 1240.00\r\n  },\r\n  \"payouts\": { \"pending_count\": 12, \"pending_amount\": 1240.00, \"paid_count\": 981, \"paid_amount\": 73873.27 },\r\n  \"pending_registrations\": 7,\r\n  \"top_affiliates\": [\r\n    { \"user_id\": 1456, \"orders_count\": 19, \"total_sales\": 1977.80, \"total_commission\": 181.60,\r\n      \"user\": { \"id\": 1456, \"display_name\": \"Sarah J\", \"login\": \"sarahj\", \"email\": \"sarah@example.com\" } }\r\n  ],\r\n  \"cached\": false\r\n}<\/code><\/pre>\r\n<p><code>cached: true<\/code> means the response came from the 5-minute cache. The <code>payouts<\/code> block is present only when the payouts add-on is available \u2014 the free build omits it entirely rather than reporting four zeros as though the program had simply never paid anybody.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Totals are summed from the same stored per-coupon snapshots the affiliate dashboards show, so they move when those snapshots are rebuilt rather than order by order. Balances (<code>unpaid_commission<\/code>, <code>pending_payout_commission<\/code>) are always current.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=events\" data-rad-page=\"events\"><span>Previous<\/span>Events<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=api-key-management\" data-rad-page=\"api-key-management\"><span>Next<\/span>API Key Management<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-api-key-management\" data-rad-page=\"api-key-management\" data-rad-title=\"API Key Management\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Endpoints<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">API Key Management<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Keys can be managed in the admin UI (<strong>Coupon Affiliates \u2192 Admin Tools \u2192 API<\/strong>) or programmatically. Every route here requires an admin with the <code>manage<\/code> scope.<\/p>\r\n\r\n<h3 id=\"list-keys\">List keys<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/keys<\/code><\/p>\r\n<p>Standard pagination. Returns metadata only \u2014 never tokens or hashes:<\/p>\r\n<pre><code>[\r\n  {\r\n    \"id\": 4,\r\n    \"user_id\": 1456,\r\n    \"description\": \"Zapier integration\",\r\n    \"key_prefix\": \"wcus_9f2c1e\",\r\n    \"scopes\": [\"read\"],\r\n    \"status\": \"active\",\r\n    \"last_used\": \"2026-08-07T13:55:00\",\r\n    \"date_created\": \"2026-08-01T09:12:00\",\r\n    \"date_expires\": null\r\n  }\r\n]<\/code><\/pre>\r\n<p><code>key_prefix<\/code> is the first 12 characters of the token \u2014 enough to recognise a key in your own logs, useless as a credential.<\/p>\r\n\r\n<h3 id=\"create-a-key\">Create a key<\/h3>\r\n<p><span class=\"rad-method rad-post\">POST<\/span> <code>\/wp-json\/wcusage\/v2\/keys<\/code><\/p>\r\n<table><thead><tr><th>Body param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>user_id<\/td><td>integer<\/td><td>The user the key acts as. Defaults to the current user. Use an affiliate's user ID to create a key limited to their own data.<\/td><\/tr>\r\n<tr><td>description<\/td><td>string<\/td><td>Label, e.g. \"Zapier integration\". Up to 200 characters.<\/td><\/tr>\r\n<tr><td>scopes<\/td><td>array<\/td><td>Any of <code>read<\/code>, <code>write<\/code>, <code>manage<\/code>. Default <code>[\"read\"]<\/code>.<\/td><\/tr>\r\n<tr><td>expires<\/td><td>date<\/td><td>Optional expiry (<code>Y-m-d<\/code>). The key stops working at the end of that day, in the site's timezone.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>{\r\n  \"id\": 4,\r\n  \"token\": \"wcus_9f2c1e4a7b3d5e6f8a9b0c1d2e3f4a5b6c7d8e9f\",\r\n  \"notice\": \"Store this token now - it cannot be shown again.\",\r\n  \"user_id\": 1456\r\n}<\/code><\/pre>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">The token is returned <strong>only once<\/strong>, at creation, with <code>201<\/code>. Only a SHA-256 hash is stored on the server, so a lost token cannot be recovered \u2014 revoke it and create another.<\/div><\/div>\r\n<p>You may always create a key for <strong>yourself<\/strong>. Creating one for another user requires the capability to edit that user, so a lower-privileged manager cannot mint a key that acts as a full administrator. Otherwise the response is <code>403 wcusage_api_cannot_create_for_user<\/code>.<\/p>\r\n\r\n<h3 id=\"revoke-a-key\">Revoke a key<\/h3>\r\n<p><span class=\"rad-method rad-delete\">DELETE<\/span> <code>\/wp-json\/wcusage\/v2\/keys\/{id}<\/code><\/p>\r\n<pre><code>{ \"revoked\": true }<\/code><\/pre>\r\n<p>Revocation is immediate: clients using the key receive <code>401<\/code> on their next request. It is idempotent \u2014 revoking an already-revoked key reports success. The same \"could you edit that user\" rule applies, so one plugin admin cannot destroy an administrator's integration credential.<\/p>\r\n<p>Key creation and revocation are both recorded in the activity log (<code>api_key_created<\/code>, <code>api_key_revoked<\/code>), so they show up in <code>\/events<\/code>.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Permanent deletion of a key row is available on the admin screen only. The API offers revocation, which keeps the audit trail intact.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reports\" data-rad-page=\"reports\"><span>Previous<\/span>Reports<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhooks-overview\" data-rad-page=\"webhooks-overview\"><span>Next<\/span>Webhooks Overview<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-webhooks-overview\" data-rad-page=\"webhooks-overview\" data-rad-title=\"Webhooks Overview\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Webhooks<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Webhooks Overview<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Webhooks are a <strong>PRO<\/strong> feature. In the free build the delivery layer is not shipped at all: the <code>\/webhooks*<\/code> routes are not registered and the Webhooks card does not appear on the API screen. The free equivalent is to poll <a href=\"#\">\/events<\/a>, which carries a cursor so you only ever fetch what is new.<\/div><\/div>\r\n<p>Webhooks push signed JSON notifications to a URL you choose, the moment something happens in your affiliate program \u2014 no polling required. Set them up under <strong>Coupon Affiliates \u2192 Admin Tools \u2192 API \u2192 Webhooks<\/strong>, or through the <a href=\"#\">management API<\/a>.<\/p>\r\n<p>Every lifecycle event in the plugin already flows through one funnel, and the webhook dispatcher listens to it. Deliveries are queued (Action Scheduler when WooCommerce provides it, otherwise WP-Cron), so your endpoint never slows down a checkout or an admin action.<\/p>\r\n\r\n<h3 id=\"available-events\">Available events<\/h3>\r\n<p>Every event below needs PRO, since webhooks themselves do. These first ones are raised by the core plugin, so they arrive on any PRO install:<\/p>\r\n<table><thead><tr><th>Event<\/th><th>Fires when<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>referral.created<\/code><\/td><td>A referred order is attributed to an affiliate.<\/td><\/tr>\r\n<tr><td><code>registration.created<\/code><\/td><td>A new affiliate registration is submitted.<\/td><\/tr>\r\n<tr><td><code>registration.accepted<\/code><\/td><td>A registration is approved.<\/td><\/tr>\r\n<tr><td><code>registration.declined<\/code><\/td><td>A registration is declined.<\/td><\/tr>\r\n<tr><td><code>commission.added<\/code><\/td><td>Commission is credited to an affiliate.<\/td><\/tr>\r\n<tr><td><code>commission.removed<\/code><\/td><td>Commission is removed (refund, cancellation, manual deduction).<\/td><\/tr>\r\n<tr><td><code>affiliate.created<\/code><\/td><td>An affiliate's coupon has been created and is ready to use. Fires after approval, and also when an admin creates an affiliate directly.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>These further events come from individual PRO add-ons, so they need that add-on to be active as well:<\/p>\r\n<table><thead><tr><th>Event<\/th><th>Fires when<\/th><th>Add-on<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>payout.requested<\/code><\/td><td>An affiliate requests a payout.<\/td><td>Payouts<\/td><\/tr>\r\n<tr><td><code>payout.paid<\/code><\/td><td>A payout is marked as paid.<\/td><td>Payouts<\/td><\/tr>\r\n<tr><td><code>payout.reversed<\/code><\/td><td>A payout is reversed.<\/td><td>Payouts<\/td><\/tr>\r\n<tr><td><code>payout.cancelled<\/code><\/td><td>A payout is cancelled and its amount returned to the unpaid balance.<\/td><td>Payouts<\/td><\/tr>\r\n<tr><td><code>affiliate.payout_details_updated<\/code><\/td><td>An affiliate changes their payout method or details.<\/td><td>Payouts<\/td><\/tr>\r\n<tr><td><code>reward.earned<\/code><\/td><td>An affiliate earns a reward or bonus.<\/td><td>Rewards<\/td><\/tr>\r\n<tr><td><code>campaign.created<\/code><\/td><td>An affiliate creates a campaign.<\/td><td>Campaigns<\/td><\/tr>\r\n<tr><td><code>directlink.created<\/code><\/td><td>An affiliate registers a direct-link domain.<\/td><td>Direct Link<\/td><\/tr>\r\n<tr><td><code>commission.mla_added<\/code><\/td><td>Multi-level commission is credited to an upline.<\/td><td>Multi-Level<\/td><\/tr>\r\n<tr><td><code>commission.mla_removed<\/code><\/td><td>Multi-level commission is removed from an upline.<\/td><td>Multi-Level<\/td><\/tr>\r\n<tr><td><code>mla.invite_created<\/code><\/td><td>A multi-level affiliate invite is created.<\/td><td>Multi-Level<\/td><\/tr>\r\n<tr><td><code>mla.sub_registered<\/code><\/td><td>Someone registers as a sub-affiliate under an existing affiliate.<\/td><td>Multi-Level<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Subscribe an endpoint to specific events, or to <code>*<\/code> for everything. The live catalog for <em>your<\/em> install is always available at <span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/webhooks\/events<\/code> \u2014 it lists only events this build can actually raise, so you never subscribe to a notification that could never arrive.<\/p>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\"><code>affiliate.payout_details_updated<\/code> arriving shortly before <code>payout.requested<\/code> is a well-known fraud pattern. It is worth watching even if you do nothing else with webhooks.<\/div><\/div>\r\n\r\n<h3 id=\"endpoint-requirements\">Endpoint requirements<\/h3>\r\n<ul>\r\n<li>The delivery URL must be <strong>HTTPS<\/strong> and publicly reachable. Loopback and private-network addresses are rejected at creation. Local development environments may use HTTP \u2014 see the <code>wcusage_api_webhook_require_https<\/code> filter.<\/li>\r\n<li>Respond with any <strong>2xx<\/strong> status within <strong>8 seconds<\/strong>. Anything else counts as a failed delivery.<\/li>\r\n<li>Do the real work asynchronously. Acknowledge first, process afterwards.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Webhook events fire from the plugin's activity funnel <em>before<\/em> the activity-log setting is consulted, so they keep working even on stores that have database logging switched off. They do stop when the API master switch is off.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=api-key-management\" data-rad-page=\"api-key-management\"><span>Previous<\/span>API Key Management<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=deliveries-security\" data-rad-page=\"deliveries-security\"><span>Next<\/span>Deliveries &amp; Security<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-deliveries-security\" data-rad-page=\"deliveries-security\" data-rad-title=\"Deliveries &amp; Security\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Webhooks<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Deliveries &amp; Security<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n\r\n<h3 id=\"payload-format\">Payload format<\/h3>\r\n<p>Every delivery is a JSON <code>POST<\/code> with the same envelope. The <code>data<\/code> block is rebuilt from the live object at delivery time, so a retry never carries stale figures:<\/p>\r\n<pre><code>POST \/your-endpoint HTTP\/1.1\r\nContent-Type: application\/json\r\nUser-Agent: CouponAffiliates-Webhook\/8.2.0\r\nX-WCUsage-Event: payout.paid\r\nX-WCUsage-Delivery: whd_66b4a1e2c3d4f5.12345678\r\nX-WCUsage-Signature: t=1786457100,v1=5f8a2b...\r\n\r\n{\r\n  \"event\": \"payout.paid\",\r\n  \"created\": \"2026-08-07T14:05:00+00:00\",\r\n  \"site\": \"https:\/\/example.com\",\r\n  \"data\": {\r\n    \"payout_id\": 321,\r\n    \"user_id\": 1456,\r\n    \"coupon_id\": 8338,\r\n    \"amount\": 40.46,\r\n    \"method\": \"PayPal\",\r\n    \"method_type\": \"paypal\",\r\n    \"status\": \"paid\",\r\n    \"date\": \"2026-08-07T09:00:00\",\r\n    \"date_paid\": \"2026-08-07T14:04:58\"\r\n  }\r\n}<\/code><\/pre>\r\n<table><thead><tr><th>Envelope field<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>event<\/td><td>The webhook event name, e.g. <code>payout.paid<\/code>.<\/td><\/tr>\r\n<tr><td>created<\/td><td>When the delivery was built, RFC\u00a03339 in UTC.<\/td><\/tr>\r\n<tr><td>site<\/td><td>The sending site's home URL \u2014 useful when one receiver serves several stores.<\/td><\/tr>\r\n<tr><td>data<\/td><td>Event-specific payload; shapes below.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n\r\n<h3 id=\"payload-shapes-by-event\">Payload shapes by event<\/h3>\r\n<table><thead><tr><th>Event(s)<\/th><th><code>data<\/code> fields<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>referral.created<\/code><\/td><td><code>order_id<\/code>, <code>coupon<\/code>, <code>user_id<\/code>, <code>commission<\/code><\/td><\/tr>\r\n<tr><td><code>commission.added<\/code>, <code>commission.removed<\/code>, <code>commission.mla_added<\/code>, <code>commission.mla_removed<\/code><\/td><td><code>coupon_id<\/code>, <code>coupon<\/code>, <code>user_id<\/code>, <code>note<\/code>, <code>order_id<\/code> (parsed from the note; <code>null<\/code> when there is none)<\/td><\/tr>\r\n<tr><td><code>payout.requested<\/code>, <code>payout.paid<\/code>, <code>payout.reversed<\/code>, <code>payout.cancelled<\/code><\/td><td><code>payout_id<\/code>, <code>user_id<\/code>, <code>coupon_id<\/code>, <code>amount<\/code>, <code>method<\/code>, <code>method_type<\/code>, <code>status<\/code>, <code>date<\/code>, <code>date_paid<\/code><\/td><\/tr>\r\n<tr><td><code>registration.created<\/code>, <code>registration.accepted<\/code>, <code>registration.declined<\/code><\/td><td><code>registration_id<\/code>, <code>user_id<\/code>, <code>coupon_code<\/code>, <code>status<\/code>, <code>type<\/code>, <code>date<\/code><\/td><\/tr>\r\n<tr><td><code>affiliate.created<\/code><\/td><td><code>user_id<\/code>, <code>coupon<\/code>, <code>coupon_id<\/code>, <code>coupon_ids<\/code> (every coupon they now hold)<\/td><\/tr>\r\n<tr><td><code>affiliate.payout_details_updated<\/code><\/td><td><code>user_id<\/code>, <code>method_type<\/code>, <code>has_details<\/code><\/td><\/tr>\r\n<tr><td><code>directlink.created<\/code><\/td><td><code>directlink_id<\/code>, <code>coupon_id<\/code>, <code>coupon<\/code>, <code>user_id<\/code>, <code>website<\/code>, <code>campaign<\/code>, <code>status<\/code><\/td><\/tr>\r\n<tr><td><code>mla.invite_created<\/code><\/td><td><code>invite_id<\/code>, <code>user_id<\/code>, <code>status<\/code>, <code>date<\/code><\/td><\/tr>\r\n<tr><td><code>mla.sub_registered<\/code><\/td><td><code>user_id<\/code>, <code>parent_user_id<\/code>, <code>coupon<\/code><\/td><\/tr>\r\n<tr><td><code>reward.earned<\/code>, <code>campaign.created<\/code><\/td><td><code>object_id<\/code>, <code>info<\/code><\/td><\/tr>\r\n<tr><td><code>ping<\/code> (test delivery)<\/td><td><code>message<\/code><\/td><\/tr>\r\n<\/tbody><\/table>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">Payloads never carry customer personal data, payout destination details, or the email address of a person who is not a user of the site. <code>mla.invite_created<\/code>, for example, deliberately omits the invitee's email address. Add fields for your own trusted receiver with the <code>wcusage_api_webhook_payload<\/code> filter.<\/div><\/div>\r\n\r\n<h3 id=\"verifying-signatures\">Verifying signatures<\/h3>\r\n<p>Each webhook has a secret (<code>whsec_\u2026<\/code>), shown in the admin UI and returned once on creation through the API. Every delivery is signed with HMAC-SHA256 over <code>\"&lt;timestamp&gt;.&lt;raw body&gt;\"<\/code>:<\/p>\r\n<pre><code>X-WCUsage-Signature: t=&lt;unix timestamp&gt;,v1=&lt;hex hmac&gt;<\/code><\/pre>\r\n<p>Verify it before trusting anything in the payload:<\/p>\r\n<pre><code>&lt;?php\r\n$secret = 'whsec_your_webhook_secret';\r\n$body   = file_get_contents( 'php:\/\/input' );\r\n$header = $_SERVER['HTTP_X_WCUSAGE_SIGNATURE'] ?? '';\r\n\r\n$parts = [];\r\nforeach ( explode( ',', $header ) as $pair ) {\r\n    [ $k, $v ] = array_pad( explode( '=', $pair, 2 ), 2, '' );\r\n    $parts[ trim( $k ) ] = trim( $v );\r\n}\r\n\r\n$expected = hash_hmac( 'sha256', ( $parts['t'] ?? '' ) . '.' . $body, $secret );\r\n\r\nif ( ! hash_equals( $expected, $parts['v1'] ?? '' ) ) {\r\n    http_response_code( 401 );\r\n    exit; \/\/ signature mismatch\r\n}\r\n\r\nif ( abs( time() - (int) ( $parts['t'] ?? 0 ) ) &gt; 300 ) {\r\n    http_response_code( 401 );\r\n    exit; \/\/ replayed or stale delivery\r\n}\r\n\r\nhttp_response_code( 200 );\r\n\/\/ then process json_decode( $body, true ) out of band<\/code><\/pre>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">Always compare with a constant-time function (<code>hash_equals<\/code>), sign over the <strong>raw<\/strong> body rather than a re-encoded copy, and reject timestamps older than a few minutes to prevent replay.<\/div><\/div>\r\n\r\n<h3 id=\"retries-and-automatic-disabling\">Retries and automatic disabling<\/h3>\r\n<ul>\r\n<li>Deliveries are queued asynchronously, so your endpoint is never in the critical path of a checkout or an admin action.<\/li>\r\n<li>A failed delivery is retried up to <strong>5 attempts<\/strong> with exponential backoff: roughly 1 minute, 4 minutes, 16 minutes, then about an hour.<\/li>\r\n<li>After <strong>25 consecutive failures<\/strong> the endpoint is automatically disabled. Re-enable it from the admin page or with a <code>PATCH<\/code> request; either resets the failure counter and clears the last error.<\/li>\r\n<li>Delivery is <strong>at-least-once<\/strong>. Duplicates are possible, so use <code>X-WCUsage-Delivery<\/code> to deduplicate if your handler is not idempotent.<\/li>\r\n<li>Ordering is not guaranteed. Two events raised close together may arrive in either order, and a retried delivery may land after a newer one.<\/li>\r\n<li>The last failure message and the failure count are visible on the admin page and in <code>GET \/webhooks<\/code>.<\/li>\r\n<\/ul>\r\n<p>Tune the two limits with the <code>wcusage_api_webhook_max_attempts<\/code> and <code>wcusage_api_webhook_max_failures<\/code> filters.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhooks-overview\" data-rad-page=\"webhooks-overview\"><span>Previous<\/span>Webhooks Overview<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api\" data-rad-page=\"webhook-management-api\"><span>Next<\/span>Webhook Management API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-webhook-management-api\" data-rad-page=\"webhook-management-api\" data-rad-title=\"Webhook Management API\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Webhooks<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Webhook Management API<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>All routes require an admin with the <code>manage<\/code> scope, except the event catalog, which needs <code>read<\/code>. <strong>PRO only<\/strong> \u2014 none of these routes are registered in the free build, so they answer <code>404 rest_no_route<\/code> there.<\/p>\r\n\r\n<h3 id=\"list-webhooks\">List webhooks<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/webhooks<\/code><\/p>\r\n<pre><code>[\r\n  {\r\n    \"id\": 1,\r\n    \"name\": \"Ops Slack relay\",\r\n    \"url\": \"https:\/\/hooks.example.net\/coupon-affiliates\",\r\n    \"events\": [\"payout.requested\", \"payout.paid\"],\r\n    \"status\": \"active\",\r\n    \"failures\": 0,\r\n    \"last_delivery\": \"2026-08-07T14:05:01\",\r\n    \"last_error\": \"\",\r\n    \"date_created\": \"2026-07-30T11:00:00\"\r\n  }\r\n]<\/code><\/pre>\r\n<p>Signing secrets are not included in list responses. Full administrators can read them on the admin page; other plugin admins see them masked.<\/p>\r\n\r\n<h3 id=\"create-a-webhook\">Create a webhook<\/h3>\r\n<p><span class=\"rad-method rad-post\">POST<\/span> <code>\/wp-json\/wcusage\/v2\/webhooks<\/code><\/p>\r\n<table><thead><tr><th>Body param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>name<\/td><td>string<\/td><td>Label for the webhook. Up to 100 characters.<\/td><\/tr>\r\n<tr><td>url<\/td><td>string<\/td><td>Required. HTTPS delivery URL. Validated against loopback and private addresses.<\/td><\/tr>\r\n<tr><td>events<\/td><td>array<\/td><td>Required. Event names from the catalog, or <code>[\"*\"]<\/code> for all. Unknown names are dropped; if nothing valid remains the request fails with <code>400 wcusage_api_no_events<\/code>.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Returns <code>201<\/code> including the signing <code>secret<\/code>. Store it \u2014 it is the only way to verify deliveries, and this is the only response that carries it.<\/p>\r\n\r\n<h3 id=\"update-a-webhook\">Update a webhook<\/h3>\r\n<p><span class=\"rad-method rad-patch\">PATCH<\/span> <code>\/wp-json\/wcusage\/v2\/webhooks\/{id}<\/code><\/p>\r\n<table><thead><tr><th>Body param<\/th><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody>\r\n<tr><td>status<\/td><td>string<\/td><td><code>active<\/code> or <code>disabled<\/code>. Re-activating resets the failure counter and clears the last error.<\/td><\/tr>\r\n<tr><td>events<\/td><td>array<\/td><td>Replaces the subscribed events entirely.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Sending neither returns <code>400 wcusage_api_no_fields<\/code>.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">The delivery <strong>URL cannot be changed<\/strong> after creation, by design \u2014 it is validated against SSRF only on the way in. To point a webhook somewhere else, delete it and create a new one. The signing secret is likewise fixed.<\/div><\/div>\r\n\r\n<h3 id=\"test-a-webhook\">Test a webhook<\/h3>\r\n<p><span class=\"rad-method rad-post\">POST<\/span> <code>\/wp-json\/wcusage\/v2\/webhooks\/{id}\/test<\/code><\/p>\r\n<p>Sends a signed <code>ping<\/code> delivery immediately and synchronously, and returns the HTTP status your endpoint answered with:<\/p>\r\n<pre><code>{ \"code\": 200 }<\/code><\/pre>\r\n<p>A transport-level failure (DNS, TLS, timeout) returns <code>502 wcusage_api_delivery_failed<\/code> with the underlying message. Test deliveries do not count towards the failure counter.<\/p>\r\n\r\n<h3 id=\"delete-a-webhook\">Delete a webhook<\/h3>\r\n<p><span class=\"rad-method rad-delete\">DELETE<\/span> <code>\/wp-json\/wcusage\/v2\/webhooks\/{id}<\/code><\/p>\r\n<pre><code>{ \"deleted\": true }<\/code><\/pre>\r\n\r\n<h3 id=\"event-catalog\">Event catalog<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/webhooks\/events<\/code><\/p>\r\n<p><strong>Permission:<\/strong> admin, <code>read<\/code> scope.<\/p>\r\n<pre><code>[\r\n  { \"event\": \"referral.created\", \"description\": \"A referred order was attributed to an affiliate.\" },\r\n  { \"event\": \"payout.paid\", \"description\": \"A payout was marked as paid.\" }\r\n]<\/code><\/pre>\r\n<p>Read this rather than hard-coding the list: it reflects exactly which add-ons are active on the install.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=deliveries-security\" data-rad-page=\"deliveries-security\"><span>Previous<\/span>Deliveries &amp; Security<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples\" data-rad-page=\"code-samples\"><span>Next<\/span>Code Samples<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-code-samples\" data-rad-page=\"code-samples\" data-rad-title=\"Code Samples\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Integrations<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Code Samples<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Nothing here needs a client library \u2014 the API is plain HTTP with a bearer token. These are complete, working starting points.<\/p>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">The samples on this page are illustrative and provided <strong>as is<\/strong>. They deliberately keep to the shortest thing that works, so they omit the logging, retry policy, input validation and secret handling a production integration needs. Review and adapt them before relying on them \u2014 see <a href=\"#\">Security &amp; Disclaimers<\/a>.<\/div><\/div>\r\n\r\n<h3 id=\"php-inside-wordpress\">PHP (inside WordPress)<\/h3>\r\n<p>Talking to another store from a WordPress site, using the HTTP API that is already loaded:<\/p>\r\n<pre><code>$response = wp_remote_get(\r\n    'https:\/\/example.com\/wp-json\/wcusage\/v2\/reports\/summary?top=5',\r\n    array(\r\n        'timeout' =&gt; 15,\r\n        'headers' =&gt; array(\r\n            'Authorization' =&gt; 'Bearer ' . WCUSAGE_API_KEY,\r\n            'Accept'        =&gt; 'application\/json',\r\n        ),\r\n    )\r\n);\r\n\r\nif ( is_wp_error( $response ) ) {\r\n    error_log( 'Coupon Affiliates API unreachable: ' . $response-&gt;get_error_message() );\r\n    return;\r\n}\r\n\r\n$code = wp_remote_retrieve_response_code( $response );\r\n$body = json_decode( wp_remote_retrieve_body( $response ), true );\r\n\r\nif ( 200 !== $code ) {\r\n    \/\/ Every plugin error carries a machine-readable code.\r\n    error_log( 'API error ' . $code . ': ' . ( $body['code'] ?? 'unknown' ) );\r\n    return;\r\n}\r\n\r\nprintf( 'Unpaid commission: %s %.2f', $body['currency'], $body['totals']['unpaid_commission'] );<\/code><\/pre>\r\n\r\n<h3 id=\"php-standalone-with-pagination\">PHP (standalone, with pagination)<\/h3>\r\n<p>Walking a collection to the end, using the pagination headers rather than guessing:<\/p>\r\n<pre><code>&lt;?php\r\nfunction wcu_api_get( $path, $params = [] ) {\r\n    $url = 'https:\/\/example.com\/wp-json\/wcusage\/v2' . $path;\r\n    if ( $params ) {\r\n        $url .= '?' . http_build_query( $params );\r\n    }\r\n\r\n    $ch = curl_init( $url );\r\n    curl_setopt_array( $ch, [\r\n        CURLOPT_RETURNTRANSFER =&gt; true,\r\n        CURLOPT_HEADER         =&gt; true,\r\n        CURLOPT_HTTPHEADER     =&gt; [ 'Authorization: Bearer ' . getenv( 'WCUSAGE_KEY' ) ],\r\n        CURLOPT_TIMEOUT        =&gt; 20,\r\n    ] );\r\n\r\n    $raw         = curl_exec( $ch );\r\n    $status      = curl_getinfo( $ch, CURLINFO_RESPONSE_CODE );\r\n    $header_size = curl_getinfo( $ch, CURLINFO_HEADER_SIZE );\r\n    curl_close( $ch );\r\n\r\n    $headers = substr( $raw, 0, $header_size );\r\n    $body    = json_decode( substr( $raw, $header_size ), true );\r\n\r\n    $pages = 1;\r\n    if ( preg_match( '\/^X-WP-TotalPages:\\s*(\\d+)\/mi', $headers, $m ) ) {\r\n        $pages = (int) $m[1];\r\n    }\r\n\r\n    return [ 'status' =&gt; $status, 'body' =&gt; $body, 'pages' =&gt; $pages ];\r\n}\r\n\r\n$page = 1;\r\ndo {\r\n    $result = wcu_api_get( '\/affiliates', [ 'page' =&gt; $page, 'per_page' =&gt; 100 ] );\r\n\r\n    if ( 200 !== $result['status'] ) {\r\n        throw new RuntimeException( 'API error: ' . ( $result['body']['code'] ?? $result['status'] ) );\r\n    }\r\n\r\n    foreach ( $result['body'] as $affiliate ) {\r\n        printf( \"%-30s %8.2f unpaid\\n\",\r\n            $affiliate['user']['display_name'],\r\n            $affiliate['unpaid_commission']\r\n        );\r\n    }\r\n\r\n    $page++;\r\n} while ( $page &lt;= $result['pages'] );<\/code><\/pre>\r\n\r\n<h3 id=\"javascript-node\">JavaScript \/ Node<\/h3>\r\n<p>Including the one piece of error handling that matters most in practice \u2014 honouring <code>retry_after<\/code>:<\/p>\r\n<pre><code>const BASE = 'https:\/\/example.com\/wp-json\/wcusage\/v2';\r\nconst KEY  = process.env.WCUSAGE_KEY;\r\n\r\nasync function api( path, params = {} ) {\r\n  const url = new URL( BASE + path );\r\n  Object.entries( params ).forEach( ( [ k, v ] ) =&gt; url.searchParams.set( k, v ) );\r\n\r\n  const res  = await fetch( url, { headers: { Authorization: `Bearer ${KEY}` } } );\r\n  const body = await res.json();\r\n\r\n  if ( res.status === 429 ) {\r\n    const wait = ( body?.data?.retry_after ?? 60 ) * 1000;\r\n    await new Promise( r =&gt; setTimeout( r, wait ) );\r\n    return api( path, params );           \/\/ one retry after the window\r\n  }\r\n\r\n  if ( ! res.ok ) {\r\n    throw new Error( `${body.code}: ${body.message}` );\r\n  }\r\n\r\n  return { body, total: Number( res.headers.get( 'X-WP-Total' ) || 0 ) };\r\n}\r\n\r\nconst { body: me } = await api( '\/me' );\r\nconsole.log( `Authenticated as ${me.user.display_name}, admin: ${me.is_admin}, scopes:`, me.auth.scopes );<\/code><\/pre>\r\n\r\n<h3 id=\"python\">Python<\/h3>\r\n<pre><code>import os, requests\r\n\r\nBASE = \"https:\/\/example.com\/wp-json\/wcusage\/v2\"\r\nSESSION = requests.Session()\r\nSESSION.headers[\"Authorization\"] = f\"Bearer {os.environ['WCUSAGE_KEY']}\"\r\n\r\ndef get(path, **params):\r\n    r = SESSION.get(f\"{BASE}{path}\", params=params, timeout=20)\r\n    if r.status_code != 200:\r\n        raise RuntimeError(f\"{r.status_code} {r.json().get('code', '')}\")\r\n    return r\r\n\r\n# All referred orders for one coupon last month, newest first.\r\npage, orders = 1, []\r\nwhile True:\r\n    r = get(\"\/coupons\/8338\/orders\", **{\"from\": \"2026-07-01\", \"to\": \"2026-07-31\",\r\n                                       \"page\": page, \"per_page\": 100})\r\n    orders += r.json()\r\n    if r.headers.get(\"X-WCUsage-Truncated\") == \"1\":\r\n        print(\"Warning: order window was capped; narrow the date range.\")\r\n    if page &gt;= int(r.headers.get(\"X-WP-TotalPages\", 1)):\r\n        break\r\n    page += 1\r\n\r\nprint(f\"{len(orders)} orders, {sum(o['commission'] for o in orders):.2f} commission\")<\/code><\/pre>\r\n\r\n<h3 id=\"command-line\">Command line<\/h3>\r\n<pre><code># Quick health check\r\ncurl -s https:\/\/example.com\/wp-json\/wcusage\/v2\/me \\\r\n  -H \"Authorization: Bearer $WCUSAGE_KEY\" | jq\r\n\r\n# Top 5 affiliates by all-time commission\r\ncurl -s \"https:\/\/example.com\/wp-json\/wcusage\/v2\/reports\/summary?top=5\" \\\r\n  -H \"Authorization: Bearer $WCUSAGE_KEY\" \\\r\n  | jq -r '.top_affiliates[] | \"\\(.user.display_name)\\t\\(.total_commission)\"'\r\n\r\n# Anything that needs paying out\r\ncurl -s \"https:\/\/example.com\/wp-json\/wcusage\/v2\/payouts?status=pending&amp;per_page=100\" \\\r\n  -H \"Authorization: Bearer $WCUSAGE_KEY\" \\\r\n  | jq -r '.[] | \"#\\(.id)\\t\\(.user.display_name)\\t\\(.amount)\\t\\(.method)\"'<\/code><\/pre>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">Keep the key in an environment variable or your platform's secret store, never in the script. If a key does leak, revoke it on the API screen \u2014 the WordPress account behind it is untouched.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=webhook-management-api\" data-rad-page=\"webhook-management-api\"><span>Previous<\/span>Webhook Management API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=no-code-platforms\" data-rad-page=\"no-code-platforms\"><span>Next<\/span>No-Code Platforms<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-no-code-platforms\" data-rad-page=\"no-code-platforms\" data-rad-title=\"No-Code Platforms\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Integrations<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">No-Code Platforms<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Zapier, Make, n8n, Pabbly Connect, Activepieces and similar tools all speak plain HTTP, so no special connector is needed. There are two ways to wire them up.<\/p>\r\n\r\n<h3 id=\"push-webhook-trigger-recommended-pro\">Push: webhook trigger (recommended, PRO)<\/h3>\r\n<ol>\r\n<li>In your automation tool, create a scenario starting with a <strong>Webhook \/ Catch Hook<\/strong> trigger and copy the URL it gives you.<\/li>\r\n<li>In WordPress, go to <strong>Coupon Affiliates \u2192 Admin Tools \u2192 API \u2192 Webhooks<\/strong> and add an endpoint with that URL, subscribed to the events you care about.<\/li>\r\n<li>Press <strong>Test<\/strong>. Your tool receives a <code>ping<\/code> payload and learns the structure.<\/li>\r\n<li>Map the fields from <code>data<\/code> into whatever comes next \u2014 a Slack message, a spreadsheet row, a CRM record.<\/li>\r\n<\/ol>\r\n<p>This is instant, costs no polling quota, and scales to any program size.<\/p>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">Most no-code tools cannot verify the HMAC signature. Treat the catch-hook URL itself as a secret: it is long and unguessable, so do not publish it, and never let the automation perform a destructive or financial action purely on the say-so of an unverified payload. Where the action matters, have the scenario read the object back from the API (for example <code>GET \/payouts\/{id}<\/code>) before acting on it.<\/div><\/div>\r\n\r\n<h3 id=\"pull-scheduled-polling\">Pull: scheduled polling<\/h3>\r\n<p>If you are on the free version, or your tool cannot receive webhooks, poll the change feed on a schedule instead:<\/p>\r\n<ol>\r\n<li>Add a <strong>Scheduled<\/strong> trigger (every 5\u201315 minutes is plenty).<\/li>\r\n<li>Add an <strong>HTTP GET<\/strong> module pointed at <code>https:\/\/example.com\/wp-json\/wcusage\/v2\/events<\/code> with the query <code>after={{ last_id }}&amp;per_page=100<\/code>, and a header <code>Authorization: Bearer wcus_\u2026<\/code>.<\/li>\r\n<li>Store the response header <code>X-WCUsage-Last-Event<\/code> in a data store \/ variable, and feed it back as <code>last_id<\/code> next run.<\/li>\r\n<li>Iterate the returned array and branch on <code>event<\/code>.<\/li>\r\n<\/ol>\r\n<p>Starting from <code>after=0<\/code> would replay the whole history, so seed the cursor once with a single unfiltered call and store the highest <code>id<\/code> you see.<\/p>\r\n\r\n<h3 id=\"writing-back\">Writing back<\/h3>\r\n<p>Actions such as approving an application are ordinary HTTP requests:<\/p>\r\n<pre><code>Method:  POST\r\nURL:     https:\/\/example.com\/wp-json\/wcusage\/v2\/registrations\/512\/status\r\nHeaders: Authorization: Bearer wcus_...\r\n         Content-Type: application\/json\r\nBody:    { \"status\": \"accepted\", \"send_email\": true }<\/code><\/pre>\r\n<p>Use a key with <code>write<\/code> for this, and keep it separate from any read-only key you use elsewhere so you can revoke one without breaking the other.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=code-samples\" data-rad-page=\"code-samples\"><span>Previous<\/span>Code Samples<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reporting-dashboards\" data-rad-page=\"reporting-dashboards\"><span>Next<\/span>Reporting &amp; Dashboards<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-reporting-dashboards\" data-rad-page=\"reporting-dashboards\" data-rad-title=\"Reporting &amp; Dashboards\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Integrations<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Reporting &amp; Dashboards<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n\r\n<h3 id=\"google-sheets\">Google Sheets<\/h3>\r\n<p>An Apps Script bound to a sheet, refreshing a leaderboard on a timer:<\/p>\r\n<pre><code>function refreshAffiliates() {\r\n  const key   = PropertiesService.getScriptProperties().getProperty( 'WCUSAGE_KEY' );\r\n  const url   = 'https:\/\/example.com\/wp-json\/wcusage\/v2\/reports\/summary?top=50';\r\n  const res   = UrlFetchApp.fetch( url, {\r\n    headers: { Authorization: 'Bearer ' + key },\r\n    muteHttpExceptions: true\r\n  } );\r\n\r\n  if ( res.getResponseCode() !== 200 ) {\r\n    throw new Error( res.getContentText() );\r\n  }\r\n\r\n  const data  = JSON.parse( res.getContentText() );\r\n  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName( 'Affiliates' );\r\n\r\n  sheet.clear();\r\n  sheet.appendRow( [ 'Affiliate', 'Orders', 'Sales', 'Commission' ] );\r\n  data.top_affiliates.forEach( function ( a ) {\r\n    sheet.appendRow( [ a.user.display_name, a.orders_count, a.total_sales, a.total_commission ] );\r\n  } );\r\n\r\n  sheet.getRange( 'A1:D1' ).setFontWeight( 'bold' );\r\n}<\/code><\/pre>\r\n<p>Add a time-driven trigger for <code>refreshAffiliates<\/code> and store the key under <strong>Project Settings \u2192 Script Properties<\/strong>, not in the code.<\/p>\r\n\r\n<h3 id=\"looker-studio-power-bi-metabase\">Looker Studio, Power BI, Metabase<\/h3>\r\n<p>All three can read a JSON\/REST source with a bearer header. Point them at:<\/p>\r\n<ul>\r\n<li><code>\/reports\/summary?top=50<\/code> for the program overview \u2014 one row of totals plus a leaderboard, already aggregated and cached for five minutes.<\/li>\r\n<li><code>\/affiliates?per_page=100<\/code> for a per-affiliate table, walking pages with <code>X-WP-TotalPages<\/code>.<\/li>\r\n<li><code>\/coupons\/{id}\/orders?from=&amp;to=<\/code> for transaction-level detail, one coupon at a time.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">Refresh no more often than every five minutes. <code>\/reports\/summary<\/code> is cached for exactly that long, so a faster schedule returns identical data (<code>cached: true<\/code>) while still spending rate limit.<\/div><\/div>\r\n\r\n<h3 id=\"warehousing-the-data\">Warehousing the data<\/h3>\r\n<p>For an incremental ETL, drive it from <code>\/events<\/code> rather than re-reading collections. Store the last event ID alongside your data, pull only what is new, and reconcile occasionally with a full pass over <code>\/affiliates<\/code>. That keeps a nightly job to a handful of requests regardless of program size.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=no-code-platforms\" data-rad-page=\"no-code-platforms\"><span>Previous<\/span>No-Code Platforms<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=notifications\" data-rad-page=\"notifications\"><span>Next<\/span>Notifications<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-notifications\" data-rad-page=\"notifications\" data-rad-title=\"Notifications\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Integrations<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Notifications<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>A small receiver that verifies the signature and relays events into Slack. The same shape works for Discord, Teams, Telegram or an SMS gateway \u2014 only the last few lines change.<\/p>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">Your receiver is a public URL that anyone can post to. Nothing proves a payload came from your store except a valid signature, so verify it and reject stale timestamps <em>before<\/em> doing anything with the contents \u2014 and escape those contents like any other external input when you pass them on.<\/div><\/div>\r\n<pre><code>&lt;?php\r\n\/\/ webhook-receiver.php\r\n$secret = getenv( 'WCUSAGE_WEBHOOK_SECRET' );\r\n$body   = file_get_contents( 'php:\/\/input' );\r\n$header = $_SERVER['HTTP_X_WCUSAGE_SIGNATURE'] ?? '';\r\n\r\n$parts = [];\r\nforeach ( explode( ',', $header ) as $pair ) {\r\n    [ $k, $v ] = array_pad( explode( '=', $pair, 2 ), 2, '' );\r\n    $parts[ trim( $k ) ] = trim( $v );\r\n}\r\n\r\n$expected = hash_hmac( 'sha256', ( $parts['t'] ?? '' ) . '.' . $body, $secret );\r\n\r\nif ( ! hash_equals( $expected, $parts['v1'] ?? '' ) || abs( time() - (int) ( $parts['t'] ?? 0 ) ) &gt; 300 ) {\r\n    http_response_code( 401 );\r\n    exit;\r\n}\r\n\r\n\/\/ Acknowledge immediately - the sender allows 8 seconds and retries on anything else.\r\nhttp_response_code( 200 );\r\nfastcgi_finish_request();\r\n\r\n$payload = json_decode( $body, true );\r\n$data    = $payload['data'] ?? [];\r\n\r\nswitch ( $payload['event'] ) {\r\n    case 'payout.requested':\r\n        $text = sprintf( ':moneybag: Payout requested: %s asked for %.2f via %s (payout #%d)',\r\n            get_display_name( $data['user_id'] ), $data['amount'], $data['method'], $data['payout_id'] );\r\n        break;\r\n\r\n    case 'registration.created':\r\n        $text = sprintf( ':wave: New affiliate application for code *%s*', $data['coupon_code'] );\r\n        break;\r\n\r\n    case 'affiliate.payout_details_updated':\r\n        $text = sprintf( ':warning: Affiliate %d changed their payout details (%s)',\r\n            $data['user_id'], $data['method_type'] ?: 'none set' );\r\n        break;\r\n\r\n    default:\r\n        return; \/\/ not interested\r\n}\r\n\r\n$ch = curl_init( getenv( 'SLACK_WEBHOOK_URL' ) );\r\ncurl_setopt_array( $ch, [\r\n    CURLOPT_POST       =&gt; true,\r\n    CURLOPT_HTTPHEADER =&gt; [ 'Content-Type: application\/json' ],\r\n    CURLOPT_POSTFIELDS =&gt; json_encode( [ 'text' =&gt; $text ] ),\r\n    CURLOPT_RETURNTRANSFER =&gt; true,\r\n] );\r\ncurl_exec( $ch );<\/code><\/pre>\r\n<p>Two details worth copying:<\/p>\r\n<ul>\r\n<li><strong>Acknowledge before working.<\/strong> <code>fastcgi_finish_request()<\/code> (or a queue) returns the 2xx immediately, so a slow Slack call never turns into a retry storm.<\/li>\r\n<li><strong>Ignore unknown events.<\/strong> New event types appear in later releases; a receiver that throws on them will disable itself after 25 failures.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">Subscribing to <code>*<\/code> and filtering in your receiver is easier to maintain than editing the subscription list every time you want another event \u2014 as long as your handler ignores what it does not recognise.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=reporting-dashboards\" data-rad-page=\"reporting-dashboards\"><span>Previous<\/span>Reporting &amp; Dashboards<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=ai-agents-assistants\" data-rad-page=\"ai-agents-assistants\"><span>Next<\/span>AI Agents &amp; Assistants<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-ai-agents-assistants\" data-rad-page=\"ai-agents-assistants\" data-rad-title=\"AI Agents &amp; Assistants\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Integrations<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">AI Agents &amp; Assistants<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>The API was designed with agents in mind: stable numeric IDs, plain JSON, self-describing errors, a machine-readable spec and a single \"who am I\" call.<\/p>\r\n\r\n<h3 id=\"setting-one-up\">Setting one up<\/h3>\r\n<ol>\r\n<li>Create a WordPress user for the agent, at the access level it genuinely needs \u2014 an affiliate account for an affiliate-facing assistant, an admin account only for a program-management assistant.<\/li>\r\n<li>Create an API key against that user with the <strong><code>read<\/code> scope only<\/strong>, and an expiry date.<\/li>\r\n<li>Give the tool the OpenAPI URL: <code>https:\/\/example.com\/wp-json\/wcusage\/v2\/openapi<\/code>. Custom GPT Actions, most agent frameworks and any OpenAPI-aware tool builder import it directly, generating one callable function per endpoint.<\/li>\r\n<li>Configure bearer authentication with the key.<\/li>\r\n<\/ol>\r\n<p>For frameworks without OpenAPI import, hand-define a handful of tools instead \u2014 <code>\/me<\/code>, <code>\/reports\/summary<\/code>, <code>\/affiliates\/{id}\/stats<\/code>, <code>\/coupons\/{id}\/orders<\/code> and <code>\/events<\/code> cover the overwhelming majority of questions.<\/p>\r\n\r\n<h3 id=\"prompting-notes\">Prompting notes<\/h3>\r\n<ul>\r\n<li>Tell the agent to call <code>\/me<\/code> first. It learns whether it is an admin or a single affiliate, and which scopes it holds, instead of guessing and hitting <code>403<\/code>s.<\/li>\r\n<li>Tell it to reference coupons by ID, not code.<\/li>\r\n<li>Tell it that <code>404<\/code> can mean \"not yours\", so it should not conclude an ID is unused.<\/li>\r\n<li>Tell it to respect <code>retry_after<\/code> on <code>429<\/code> rather than retrying immediately \u2014 otherwise a loop burns the whole rate limit.<\/li>\r\n<li>Point it at <code>\/reports\/summary<\/code> for \"how are we doing\" questions. Left to itself an agent will happily page through every affiliate to compute a total the API already has.<\/li>\r\n<\/ul>\r\n\r\n<h3 id=\"safety\">Safety<\/h3>\r\n<p>An agent acts on the text it reads, and some of that text comes from outside your control \u2014 an applicant's \"how will you promote us\" answer, a campaign name, a website URL an affiliate typed in. An agent with write access can be steered by content like that into doing something you never asked for. Read-only access removes the problem entirely; anything more needs a person in the loop on the actions that matter.<\/p>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">Never give an agent the <code>manage<\/code> scope. A key holding it can mint further keys with any scope and point a webhook at any server \u2014 that is full account access, in the hands of something driven by text it reads from the outside world.<\/div><\/div>\r\n<p>If an agent must act, grant <code>write<\/code> deliberately and remember what it can then do: approve or decline applications, create payout requests, and change payout statuses. Payout <em>status<\/em> changes never contact a gateway, but on a store with automatic payouts a <em>created<\/em> payout request can pay real money immediately. If that is a concern, switch the <code>\/payouts<\/code> endpoints off on the API screen and let the agent read everything else.<\/p>\r\n<div class=\"rad-callout rad-callout-tip\"><span class=\"rad-callout-label\">Tip<\/span><div class=\"rad-callout-body\">An affiliate-facing assistant is the safest deployment by construction: give it a read-only key on the affiliate's own account, and the API itself guarantees it can never see anybody else's data, however it is prompted.<\/div><\/div>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=notifications\" data-rad-page=\"notifications\"><span>Previous<\/span>Notifications<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes\" data-rad-page=\"recipes\"><span>Next<\/span>Recipes<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-recipes\" data-rad-page=\"recipes\" data-rad-title=\"Recipes\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Integrations<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Recipes<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n\r\n<h3 id=\"sync-new-referrals-into-another-system\">Sync new referrals into another system<\/h3>\r\n<p>Poll the change feed and keep a cursor \u2014 one indexed query per poll, no matter how busy the program is:<\/p>\r\n<pre><code>curl -s \"https:\/\/example.com\/wp-json\/wcusage\/v2\/events?after=9912&amp;event=referral&amp;per_page=100\" \\\r\n  -H \"Authorization: Bearer $WCUSAGE_KEY\" -D headers.txt\r\n\r\n# the new cursor for next time\r\ngrep -i '^X-WCUsage-Last-Event' headers.txt<\/code><\/pre>\r\n<p>On PRO, subscribe a webhook to <code>referral.created<\/code> and skip the polling entirely.<\/p>\r\n\r\n<h3 id=\"build-a-monthly-affiliate-statement\">Build a monthly affiliate statement<\/h3>\r\n<pre><code># totals for the month\r\nGET \/wp-json\/wcusage\/v2\/affiliates\/1456\/stats?from=2026-07-01&amp;to=2026-07-31\r\n\r\n# the orders behind them, one coupon at a time\r\nGET \/wp-json\/wcusage\/v2\/coupons\/8338\/orders?from=2026-07-01&amp;to=2026-07-31&amp;per_page=100<\/code><\/pre>\r\n<p>Keep the range fixed between calls so both are served from cache rather than rescanned.<\/p>\r\n\r\n<h3 id=\"auto-approve-applications-that-meet-your-criteria\">Auto-approve applications that meet your criteria<\/h3>\r\n<pre><code># 1. find pending applications\r\nGET \/wp-json\/wcusage\/v2\/registrations?status=pending&amp;per_page=100\r\n\r\n# 2. approve the ones that qualify\r\nPOST \/wp-json\/wcusage\/v2\/registrations\/512\/status\r\n{ \"status\": \"accepted\", \"message\": \"Welcome aboard!\", \"send_email\": true }<\/code><\/pre>\r\n<p>Needs an admin key with <code>write<\/code>. Remember that accepting is one-way through the API, so apply your criteria before calling, not after.<\/p>\r\n\r\n<h3 id=\"give-an-affiliate-read-only-api-access-to-their-own-data\">Give an affiliate read-only API access to their own data<\/h3>\r\n<pre><code>POST \/wp-json\/wcusage\/v2\/keys\r\n{ \"user_id\": 1456, \"description\": \"Sarah's reporting script\", \"scopes\": [\"read\"], \"expires\": \"2027-01-01\" }<\/code><\/pre>\r\n<p>The resulting key can call <code>\/me<\/code>, <code>\/affiliates\/1456\/stats<\/code>, its own coupons, their orders and click stats, and <code>\/payouts<\/code> filtered to itself \u2014 and nothing else. Creating it requires <code>manage<\/code> plus the capability to edit that user.<\/p>\r\n\r\n<h3 id=\"reconcile-payouts-with-your-accounting-system\">Reconcile payouts with your accounting system<\/h3>\r\n<pre><code># everything paid in a period\r\nGET \/wp-json\/wcusage\/v2\/payouts?status=paid&amp;from=2026-07-01&amp;to=2026-07-31&amp;per_page=100<\/code><\/pre>\r\n<p>Match on <code>transaction_id<\/code> where your gateway provides one, and on <code>id<\/code> otherwise. <code>date<\/code> is when the payout was requested and <code>date_paid<\/code> when it was settled \u2014 use the latter for period allocation.<\/p>\r\n\r\n<h3 id=\"flag-a-payout-fraud-pattern\">Flag a payout-fraud pattern<\/h3>\r\n<p>Subscribe a webhook to <code>affiliate.payout_details_updated<\/code> and <code>payout.requested<\/code>. When both arrive for the same <code>user_id<\/code> within a short window, hold the payout for manual review:<\/p>\r\n<pre><code>POST \/wp-json\/wcusage\/v2\/payouts\/321\/status\r\n{ \"status\": \"cancel\" }<\/code><\/pre>\r\n<p>Cancelling returns the amount to the affiliate's unpaid balance, so nothing is lost; re-open it with <code>pending<\/code> once you are satisfied.<\/p>\r\n\r\n<h3 id=\"refresh-stale-coupon-statistics-nightly\">Refresh stale coupon statistics nightly<\/h3>\r\n<pre><code>GET \/wp-json\/wcusage\/v2\/coupons\/8338\/stats?refresh=true<\/code><\/pre>\r\n<p>Rebuilds the stored snapshot from the order history and saves it, so the affiliate's dashboard and every later API read are current. Needs the <code>write<\/code> scope, and is limited to one rebuild per coupon per minute \u2014 space the calls out, and treat <code>source: \"throttled\"<\/code> as \"try this one again later\".<\/p>\r\n\r\n<h3 id=\"check-the-health-of-your-integration\">Check the health of your integration<\/h3>\r\n<pre><code>GET \/wp-json\/wcusage\/v2\/me            # what am I, and what may I do?\r\nGET \/wp-json\/wcusage\/v2\/webhooks      # failures, last_error, last_delivery\r\nGET \/wp-json\/wcusage\/v2\/keys          # last_used, status, date_expires<\/code><\/pre>\r\n<p>A webhook with a rising <code>failures<\/code> count, or a key whose <code>last_used<\/code> has gone stale, is usually the first sign that something upstream broke.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=ai-agents-assistants\" data-rad-page=\"ai-agents-assistants\"><span>Previous<\/span>AI Agents &amp; Assistants<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=openapi-document\" data-rad-page=\"openapi-document\"><span>Next<\/span>OpenAPI Document<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-openapi-document\" data-rad-page=\"openapi-document\" data-rad-title=\"OpenAPI Document\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Developer Reference<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">OpenAPI Document<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n\r\n<h3 id=\"fetching-the-spec\">Fetching the spec<\/h3>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/wcusage\/v2\/openapi<\/code><\/p>\r\n<p>Returns an <strong>OpenAPI 3.1<\/strong> document generated live from the registered routes \u2014 every path, parameter, enum, default and required flag, plus both security schemes (bearer API key and Basic application password). Because it is generated from the actual route definitions, it never drifts from the implementation, and it automatically reflects which endpoints you have switched off and which add-ons are active.<\/p>\r\n<p>The document is public by default, since the WordPress REST index already enumerates routes. Restrict it to admins with:<\/p>\r\n<pre><code>add_filter( 'wcusage_api_openapi_public', '__return_false' );<\/code><\/pre>\r\n<p>Amend the generated spec \u2014 to add descriptions, tags or examples \u2014 with the <code>wcusage_api_openapi_spec<\/code> filter.<\/p>\r\n\r\n<h3 id=\"what-it-contains\">What it contains<\/h3>\r\n<pre><code>{\r\n  \"openapi\": \"3.1.0\",\r\n  \"info\": {\r\n    \"title\": \"Coupon Affiliates REST API\",\r\n    \"description\": \"Affiliate data for WooCommerce: affiliates, coupons, referred orders, commission, payouts, registrations, clicks, events and reports.\",\r\n    \"version\": \"8.2.0\"\r\n  },\r\n  \"servers\": [ { \"url\": \"https:\/\/example.com\/wp-json\/wcusage\/v2\" } ],\r\n  \"paths\": {\r\n    \"\/coupons\/{id}\/stats\": {\r\n      \"get\": {\r\n        \"operationId\": \"get_coupons_id_stats\",\r\n        \"parameters\": [\r\n          { \"name\": \"id\", \"in\": \"path\", \"required\": true, \"schema\": { \"type\": \"integer\" } },\r\n          { \"name\": \"from\", \"in\": \"query\", \"required\": false, \"schema\": { \"type\": \"string\" } },\r\n          { \"name\": \"refresh\", \"in\": \"query\", \"required\": false, \"schema\": { \"type\": \"boolean\", \"default\": false } }\r\n        ],\r\n        \"security\": [ { \"bearerApiKey\": [] }, { \"basicAppPassword\": [] } ],\r\n        \"responses\": { \"200\": { \"description\": \"Success.\" }, \"401\": { \"description\": \"Authentication required or invalid.\" }, \"403\": { \"description\": \"Insufficient permissions or scope.\" } }\r\n      }\r\n    }\r\n  },\r\n  \"components\": { \"securitySchemes\": { \"bearerApiKey\": { \"type\": \"http\", \"scheme\": \"bearer\" }, \"basicAppPassword\": { \"type\": \"http\", \"scheme\": \"basic\" } } }\r\n}<\/code><\/pre>\r\n<p>Path parameters, query parameters and JSON request bodies are all derived from the routes' own argument definitions, including types, enums, defaults and required flags. Because the document is built per request, it reflects exactly what <em>this<\/em> install exposes: endpoints switched off in the settings are absent, and so are routes belonging to add-ons that are not active.<\/p>\r\n<p>Using it is covered under <a href=\"#\">AI Agents &amp; Assistants<\/a> and <a href=\"#\">No-Code Platforms<\/a> \u2014 most tools that accept an OpenAPI URL will generate a working client from it directly.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=recipes\" data-rad-page=\"recipes\"><span>Previous<\/span>Recipes<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=availability\" data-rad-page=\"availability\"><span>Next<\/span>Availability<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-availability\" data-rad-page=\"availability\" data-rad-title=\"Availability\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Developer Reference<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Availability<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>Not every endpoint exists on every install. Three things decide it: the plugin edition, which add-ons are active, and which endpoints an administrator has switched on.<\/p>\r\n<table><thead><tr><th>Endpoint<\/th><th>Free<\/th><th>PRO<\/th><th>Notes<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>\/me<\/code>, <code>\/openapi<\/code><\/td><td>Yes<\/td><td>Yes<\/td><td><\/td><\/tr>\r\n<tr><td><code>\/affiliates<\/code>, <code>\/coupons<\/code>, <code>\/reports\/summary<\/code><\/td><td>Yes<\/td><td>Yes<\/td><td>PRO adds <code>mla_parents<\/code> to the affiliate detail view and the <code>payouts<\/code> block to the report.<\/td><\/tr>\r\n<tr><td><code>\/registrations<\/code><\/td><td>Yes<\/td><td>Yes<\/td><td><code>501<\/code> when the registrations table is absent.<\/td><\/tr>\r\n<tr><td><code>\/clicks\/stats<\/code><\/td><td>Yes<\/td><td>Yes<\/td><td><code>501<\/code> when the clicks table is absent.<\/td><\/tr>\r\n<tr><td><code>\/events<\/code><\/td><td>Yes<\/td><td>Yes<\/td><td><code>501<\/code> when the activity table is absent; <code>400<\/code> when the activity log is switched off.<\/td><\/tr>\r\n<tr><td><code>\/keys<\/code><\/td><td>Yes<\/td><td>Yes<\/td><td><\/td><\/tr>\r\n<tr><td><code>\/webhooks<\/code><\/td><td>\u2014<\/td><td>Yes<\/td><td>Not registered at all in the free build. Poll <code>\/events<\/code> instead.<\/td><\/tr>\r\n<tr><td><code>\/payouts<\/code><\/td><td>\u2014<\/td><td>Yes<\/td><td>Not registered at all in the free build; <code>501<\/code> when the add-on is inactive.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>MLA-related access (an upline reading a downline's coupon) and the add-on webhook events likewise exist only where those add-ons do. The safest way to discover what a given install offers is to read <code>\/openapi<\/code> and <code>\/webhooks\/events<\/code> rather than assuming.<\/p>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=openapi-document\" data-rad-page=\"openapi-document\"><span>Previous<\/span>OpenAPI Document<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=error-reference\" data-rad-page=\"error-reference\"><span>Next<\/span>Error Reference<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-error-reference\" data-rad-page=\"error-reference\" data-rad-title=\"Error Reference\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Developer Reference<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Error Reference<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>All codes are prefixed <code>wcusage_api_<\/code> in the response. Core WordPress codes such as <code>rest_no_route<\/code>, <code>rest_invalid_param<\/code> and <code>rest_missing_callback_param<\/code> can also appear.<\/p>\r\n<table><thead><tr><th>Code<\/th><th>Status<\/th><th>Meaning<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>unauthorized<\/code><\/td><td>401<\/td><td>No authenticated user. Send an application password or an API key.<\/td><\/tr>\r\n<tr><td><code>forbidden<\/code><\/td><td>403<\/td><td>Authenticated, but this user may not access the resource.<\/td><\/tr>\r\n<tr><td><code>invalid_key<\/code><\/td><td>401<\/td><td>The API key is unknown, revoked or expired.<\/td><\/tr>\r\n<tr><td><code>invalid_key_user<\/code><\/td><td>401<\/td><td>The user the key acts as no longer exists.<\/td><\/tr>\r\n<tr><td><code>https_required<\/code><\/td><td>401<\/td><td>API keys may only be used over HTTPS.<\/td><\/tr>\r\n<tr><td><code>too_many_auth_failures<\/code><\/td><td>429<\/td><td>Too many invalid keys from this address; wait for the window to roll over.<\/td><\/tr>\r\n<tr><td><code>insufficient_scope<\/code><\/td><td>403<\/td><td>The key lacks the scope this route needs.<\/td><\/tr>\r\n<tr><td><code>key_wrong_namespace<\/code><\/td><td>403<\/td><td>An API key was presented to an endpoint outside the plugin's namespaces.<\/td><\/tr>\r\n<tr><td><code>rate_limited<\/code><\/td><td>429<\/td><td>Per-minute request limit exceeded. See <code>retry_after<\/code>.<\/td><\/tr>\r\n<tr><td><code>throttled<\/code><\/td><td>429<\/td><td>An expensive recalculation was requested again too soon. See <code>retry_after<\/code>.<\/td><\/tr>\r\n<tr><td><code>not_found<\/code><\/td><td>404<\/td><td>No such resource \u2014 or it belongs to somebody else.<\/td><\/tr>\r\n<tr><td><code>unavailable<\/code><\/td><td>501<\/td><td>The feature or table this endpoint reads is not present on this install.<\/td><\/tr>\r\n<tr><td><code>log_disabled<\/code><\/td><td>400<\/td><td>The activity log is switched off, so the events feed is empty.<\/td><\/tr>\r\n<tr><td><code>coupon_required<\/code><\/td><td>400<\/td><td>Non-admin callers must pass a <code>coupon_id<\/code> to <code>\/clicks\/stats<\/code>.<\/td><\/tr>\r\n<tr><td><code>no_change<\/code><\/td><td>400<\/td><td>The payout or registration already has the requested status.<\/td><\/tr>\r\n<tr><td><code>already_accepted<\/code><\/td><td>400<\/td><td>The registration was already accepted; reversal is admin-only.<\/td><\/tr>\r\n<tr><td><code>no_coupon_code<\/code><\/td><td>409<\/td><td>The registration has no coupon code, so its status cannot change.<\/td><\/tr>\r\n<tr><td><code>not_updated<\/code><\/td><td>409<\/td><td>Something on the site refused the registration status change.<\/td><\/tr>\r\n<tr><td><code>payouts_disabled<\/code><\/td><td>400<\/td><td>Payout requests are switched off in the plugin settings.<\/td><\/tr>\r\n<tr><td><code>requests_disabled<\/code><\/td><td>403<\/td><td>Affiliates cannot self-request payouts on this store.<\/td><\/tr>\r\n<tr><td><code>no_affiliate<\/code><\/td><td>400<\/td><td>The coupon has no affiliate assigned.<\/td><\/tr>\r\n<tr><td><code>no_balance<\/code><\/td><td>400<\/td><td>There is no unpaid commission to request.<\/td><\/tr>\r\n<tr><td><code>below_threshold<\/code><\/td><td>400<\/td><td>The unpaid balance is under the store's minimum payout threshold.<\/td><\/tr>\r\n<tr><td><code>no_payout_details<\/code><\/td><td>400<\/td><td>The affiliate has not saved payout details.<\/td><\/tr>\r\n<tr><td><code>method_disabled<\/code><\/td><td>400<\/td><td>The saved payout method is no longer enabled on the store.<\/td><\/tr>\r\n<tr><td><code>invoice_required<\/code><\/td><td>400<\/td><td>This payout method requires an invoice upload; use the affiliate dashboard.<\/td><\/tr>\r\n<tr><td><code>in_progress<\/code><\/td><td>409<\/td><td>A payout request for this coupon is already being processed.<\/td><\/tr>\r\n<tr><td><code>not_created<\/code><\/td><td>409<\/td><td>The payout request was refused by a filter or a failed write.<\/td><\/tr>\r\n<tr><td><code>invalid_transition<\/code><\/td><td>400<\/td><td>That payout status change is not permitted through the API.<\/td><\/tr>\r\n<tr><td><code>insufficient_unpaid<\/code><\/td><td>409<\/td><td>A cancelled payout cannot be re-opened; its amount is no longer in the unpaid balance.<\/td><\/tr>\r\n<tr><td><code>conflict<\/code><\/td><td>409<\/td><td>The payout status changed while this request was in flight.<\/td><\/tr>\r\n<tr><td><code>cannot_create_for_user<\/code><\/td><td>403<\/td><td>You may not create an API key acting as that user.<\/td><\/tr>\r\n<tr><td><code>cannot_manage_key<\/code><\/td><td>403<\/td><td>You may not revoke that user's API key.<\/td><\/tr>\r\n<tr><td><code>invalid_user<\/code><\/td><td>400<\/td><td>The user for this API key does not exist.<\/td><\/tr>\r\n<tr><td><code>invalid_expiry<\/code><\/td><td>400<\/td><td>The expiry date is not in <code>Y-m-d<\/code> format.<\/td><\/tr>\r\n<tr><td><code>invalid_url<\/code><\/td><td>400<\/td><td>The webhook URL is invalid or points at a disallowed host.<\/td><\/tr>\r\n<tr><td><code>no_events<\/code><\/td><td>400<\/td><td>No valid webhook events were supplied.<\/td><\/tr>\r\n<tr><td><code>no_fields<\/code><\/td><td>400<\/td><td>A webhook update supplied nothing to change.<\/td><\/tr>\r\n<tr><td><code>delivery_failed<\/code><\/td><td>502<\/td><td>A test delivery could not reach the endpoint.<\/td><\/tr>\r\n<tr><td><code>no_entropy<\/code><\/td><td>500<\/td><td>The server could not generate a secure token or secret.<\/td><\/tr>\r\n<tr><td><code>db_error<\/code><\/td><td>500<\/td><td>The API key could not be saved.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=availability\" data-rad-page=\"availability\"><span>Previous<\/span>Availability<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=hooks-filters\" data-rad-page=\"hooks-filters\"><span>Next<\/span>Hooks &amp; Filters<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-hooks-filters\" data-rad-page=\"hooks-filters\" data-rad-title=\"Hooks &amp; Filters\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Developer Reference<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Hooks &amp; Filters<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n\r\n<h3 id=\"behaviour-filters\">Behaviour filters<\/h3>\r\n<table><thead><tr><th>Filter<\/th><th>Default<\/th><th>Purpose<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>wcusage_api_rate_limit<\/code><\/td><td>120 \/ 30<\/td><td>Requests per minute. Receives the default and the bucket identity (<code>key_*<\/code>, <code>user_*<\/code>, <code>ip_*<\/code>).<\/td><\/tr>\r\n<tr><td><code>wcusage_api_max_auth_failures<\/code><\/td><td>20<\/td><td>Invalid-key attempts allowed per address per 15 minutes. Zero or less disables the check.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_require_https<\/code><\/td><td>true outside local\/dev<\/td><td>Whether bearer keys require HTTPS.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_max_order_rows<\/code><\/td><td>5000<\/td><td>How many referred orders one <code>\/coupons\/{id}\/orders<\/code> request may consider.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_report_batch_size<\/code><\/td><td>200<\/td><td>How many coupons <code>\/reports\/summary<\/code> primes meta for at a time. Lower it on very large programs with tight memory.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_openapi_public<\/code><\/td><td>true<\/td><td>Whether <code>\/openapi<\/code> is publicly readable.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_webhook_require_https<\/code><\/td><td>true outside local\/dev<\/td><td>Whether webhook URLs must be HTTPS.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_webhook_max_attempts<\/code><\/td><td>5<\/td><td>Delivery attempts per event.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_webhook_max_failures<\/code><\/td><td>25<\/td><td>Consecutive failures before an endpoint is auto-disabled.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_webhook_events<\/code><\/td><td>\u2014<\/td><td>Extend or modify the webhook event catalog.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_auth_failure_buckets<\/code>, <code>wcusage_api_ip_buckets<\/code><\/td><td>256<\/td><td>How many buckets failed attempts and anonymous callers are spread across.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<p>Enabling the API, and enabling individual endpoints, is done from <strong>Coupon Affiliates \u2192 Admin Tools \u2192 API<\/strong> rather than by a filter. Those settings live in the <code>wcusage_api_settings<\/code> option.<\/p>\r\n\r\n<h3 id=\"response-filters\">Response filters<\/h3>\r\n<table><thead><tr><th>Filter<\/th><th>Applies to<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>wcusage_api_coupon_summary<\/code><\/td><td>Every coupon object the API returns.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_prepare_affiliate<\/code><\/td><td>Affiliate objects. Receives the detailed flag.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_prepare_payout<\/code><\/td><td>Payout objects.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_prepare_registration<\/code><\/td><td>Registration objects.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_order_item<\/code><\/td><td>Referred-order rows \u2014 the place to add extra fields for a trusted integration.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_report_summary<\/code><\/td><td>The <code>\/reports\/summary<\/code> payload.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_openapi_spec<\/code><\/td><td>The generated OpenAPI document.<\/td><\/tr>\r\n<tr><td><code>wcusage_api_webhook_payload<\/code><\/td><td>Webhook payloads, just before delivery.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>\/\/ Add the affiliate's country to every coupon the API returns.\r\nadd_filter( 'wcusage_api_coupon_summary', function ( $data, $coupon_id ) {\r\n    $user_id = (int) $data['user_id'];\r\n    $data['country'] = $user_id ? get_user_meta( $user_id, 'billing_country', true ) : '';\r\n    return $data;\r\n}, 10, 2 );<\/code><\/pre>\r\n<div class=\"rad-callout rad-callout-warning\"><span class=\"rad-callout-label\">Warning<\/span><div class=\"rad-callout-body\">These filters run for <strong>every<\/strong> caller of the affected endpoint, not only for your own integration. Several of them cover endpoints an affiliate or a multi-level upline can reach, so anything you add there is disclosed to them too. Check who can call the endpoint before adding customer data, payout details or another affiliate's information \u2014 the omissions in the default responses are deliberate.<\/div><\/div>\r\n\r\n<h3 id=\"actions\">Actions<\/h3>\r\n<table><thead><tr><th>Action<\/th><th>Fires<\/th><\/tr><\/thead><tbody>\r\n<tr><td><code>wcusage_activity_recorded<\/code><\/td><td>For every lifecycle event, with <code>( $event, $event_id, $info, $actor_id )<\/code>. This is the same funnel the webhook dispatcher listens to, and it fires whether or not database logging is enabled \u2014 hook it to build a custom integration with no polling and no HTTP.<\/td><\/tr>\r\n<\/tbody><\/table>\r\n<pre><code>add_action( 'wcusage_activity_recorded', function ( $event, $event_id, $info, $actor_id ) {\r\n    if ( 'payout_request' === $event ) {\r\n        \/\/ $event_id is the payout ID.\r\n    }\r\n}, 10, 4 );<\/code><\/pre>\r\n\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=error-reference\" data-rad-page=\"error-reference\"><span>Previous<\/span>Error Reference<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-next\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=legacy-v1-api\" data-rad-page=\"legacy-v1-api\"><span>Next<\/span>Legacy v1 API<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<section class=\"rad-page\" id=\"rad-page-legacy-v1-api\" data-rad-page=\"legacy-v1-api\" data-rad-title=\"Legacy v1 API\" hidden>\n\t\t\t\t\t<span class=\"rad-page-cat\">Developer Reference<\/span>\n\t\t\t\t\t<h1 class=\"rad-page-title\">Legacy v1 API<\/h1>\n\t\t\t\t\t<div class=\"rad-page-content\">\n\t\t\t\t\t\t\r\n<p>The original three endpoints under <code>woo-coupon-usage\/v1<\/code> remain available for backwards compatibility. They predate scopes, pagination and schemas, and they require a full <strong>administrator<\/strong> account \u2014 not merely the plugin's configurable admin capability.<\/p>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/woo-coupon-usage\/v1\/coupon-info?coupon_id={id}<\/code><\/p>\r\n<pre><code>{\r\n  \"coupon_name\": \"sarah10\",\r\n  \"unpaid_commission\": 40.46,\r\n  \"pending_payouts\": 0,\r\n  \"coupon_user_id\": 1456,\r\n  \"referral_url\": \"https:\/\/example.com\/affiliate-dashboard\/?couponid=sarah10\"\r\n}<\/code><\/pre>\r\n<p><span class=\"rad-method rad-get\">GET<\/span> <code>\/wp-json\/woo-coupon-usage\/v1\/users-coupons?user={login}<\/code> \u2014 an array of coupon IDs assigned to that login. An unknown login returns an empty array.<\/p>\r\n<p><span class=\"rad-method rad-post\">POST<\/span> <code>\/wp-json\/woo-coupon-usage\/v1\/request-payout<\/code> \u2014 body <code>coupon_id<\/code> and <code>user<\/code> (login). Returns <code>1<\/code> when a payout request was submitted and <code>0<\/code> otherwise, with no explanation of why. PRO only.<\/p>\r\n<h3 id=\"how-v1-relates-to-v2\">How v1 relates to v2<\/h3>\r\n<ul>\r\n<li>The v1 routes are <strong>not<\/strong> affected by the API master switch, so existing integrations keep working whether or not v2 is enabled.<\/li>\r\n<li>API keys, however, belong to v2: while the API is switched off they authenticate nothing at all, v1 included. v1 remains reachable with an application password or cookie.<\/li>\r\n<li>When a key <em>is<\/em> used on v1, scopes are enforced with a fail-closed default: reads need <code>read<\/code>, writes need <code>write<\/code>.<\/li>\r\n<li>Both namespaces share the same rate limiter.<\/li>\r\n<\/ul>\r\n<div class=\"rad-callout rad-callout-note\"><span class=\"rad-callout-label\">Note<\/span><div class=\"rad-callout-body\">New integrations should use <code>wcusage\/v2<\/code>. It adds affiliate self-service access, API keys and scopes, pagination and filtering, meaningful error codes, webhooks and an OpenAPI document. The v1 routes exist for existing integrations only and will not gain features.<\/div><\/div>\r\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"rad-pager\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<a class=\"rad-pager-link rad-pager-prev\" href=\"https:\/\/couponaffiliates.com\/de\/api-docs\/?doc=hooks-filters\" data-rad-page=\"hooks-filters\"><span>Previous<\/span>Hooks &amp; Filters<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/section>\n\t\t\t\t\t<\/main>\n\t<\/div>\n\t<script type=\"application\/ld+json\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"APIReference\",\"name\":\"Introduction\",\"headline\":\"Introduction\",\"url\":\"https:\\\/\\\/couponaffiliates.com\\\/de\\\/api-docs\\\/?doc=introduction\",\"description\":\"The Coupon Affiliates REST API gives external tools, scripts and AI agents structured JSON access to your affiliate program: affiliates, coupons, referr\\u2026\",\"articleSection\":\"Getting Started\",\"isPartOf\":{\"@type\":\"WebSite\",\"name\":\"Coupon Affiliates\",\"url\":\"https:\\\/\\\/couponaffiliates.com\\\/de\\\/\"}},{\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"API Documentation\",\"item\":\"https:\\\/\\\/couponaffiliates.com\\\/de\\\/api-docs\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Getting Started\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Introduction\",\"item\":\"https:\\\/\\\/couponaffiliates.com\\\/de\\\/api-docs\\\/?doc=introduction\"}]}]}<\/script>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","footnotes":"","_links_to":"","_links_to_target":""},"class_list":["post-24372","page","type-page","status-publish","hentry","wpbf-post"],"taxonomy_info":[],"featured_image_src_large":false,"author_info":{"display_name":"Elliot Sowersby","author_link":"#"},"comment_info":0,"_links":{"self":[{"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/pages\/24372","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/comments?post=24372"}],"version-history":[{"count":1,"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/pages\/24372\/revisions"}],"predecessor-version":[{"id":24373,"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/pages\/24372\/revisions\/24373"}],"wp:attachment":[{"href":"https:\/\/couponaffiliates.com\/de\/wp-json\/wp\/v2\/media?parent=24372"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}