{"id":20409,"date":"2025-10-30T09:41:48","date_gmt":"2025-10-30T08:41:48","guid":{"rendered":"https:\/\/www.welaunch.io\/en\/?post_type=faq&#038;p=20409"},"modified":"2025-10-30T09:43:08","modified_gmt":"2025-10-30T08:43:08","slug":"using-the-reward-points-rest-api","status":"publish","type":"faq","link":"https:\/\/www.welaunch.io\/en\/knowledge-base\/faq\/using-the-reward-points-rest-api\/","title":{"rendered":"Using the Reward Points REST API"},"content":{"rendered":"<p class=\"lead\">Add reward points to users via a secure REST endpoint exposed by the plugin.<\/p>\n<section>\n<h2>What does this API do?<\/h2>\n<p>It exposes a REST endpoint that <strong>adds reward points<\/strong> to a WooCommerce user and returns the user\u2019s new total.<br \/>\nInternally it reuses the plugin\u2019s <code class=\"inline\">manually_add_user_points()<\/code> logic so totals, logs, and hooks behave like a manual admin add.<\/p>\n<\/section>\n<section>\n<h2>What is the endpoint?<\/h2>\n<ul>\n<li><strong>Method:<\/strong> <span class=\"kbd\">POST<\/span><\/li>\n<li><strong>Route:<\/strong> <span class=\"kbd\">\/wp-json\/wc-reward-points\/v1\/add-points<\/span><\/li>\n<\/ul>\n<\/section>\n<section>\n<h2>What permissions are required?<\/h2>\n<p>Only <strong>authenticated users who can <code class=\"inline\">manage_woocommerce<\/code><\/strong> may call this endpoint (typically Administrators or Shop Managers). Unauthorized requests are rejected.<\/p>\n<\/section>\n<section>\n<h2>What request parameters are required?<\/h2>\n<p>All parameters are <strong>required<\/strong> and validated on the server:<\/p>\n<table>\n<thead>\n<tr>\n<th>Field<\/th>\n<th>Type<\/th>\n<th>Rules<\/th>\n<th>Purpose<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>user_id<\/code><\/td>\n<td>integer<\/td>\n<td>Must exist in WordPress<\/td>\n<td>Which user to credit<\/td>\n<\/tr>\n<tr>\n<td><code>points<\/code><\/td>\n<td>integer<\/td>\n<td>Must be a positive integer (&gt; 0)<\/td>\n<td>How many points to add<\/td>\n<\/tr>\n<tr>\n<td><code>message<\/code><\/td>\n<td>string<\/td>\n<td>Sanitized server-side (<code>sanitize_text_field<\/code>)<\/td>\n<td>Shown in the points log\/note<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Example JSON body<\/strong><\/p>\n<pre>{\r\n  \"user_id\": 123,\r\n  \"points\": 250,\r\n  \"message\": \"Customer service gesture for delayed shipment\"\r\n}<\/pre>\n<\/section>\n<section>\n<h2>What does a successful response look like?<\/h2>\n<pre>{\r\n  \"success\": true,\r\n  \"user_id\": 123,\r\n  \"points_added\": 250,\r\n  \"new_total\": 1420,\r\n  \"message\": \"Customer service gesture for delayed shipment\"\r\n}<\/pre>\n<p class=\"small\"><em>Note:<\/em> <code class=\"inline\">new_total<\/code> is read from user meta after the add completes.<\/p>\n<\/section>\n<section>\n<h2>What errors might I see?<\/h2>\n<table>\n<thead>\n<tr>\n<th>HTTP<\/th>\n<th>Code<\/th>\n<th>When it happens<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>400<\/td>\n<td><code>invalid_user<\/code><\/td>\n<td><code>user_id<\/code> is missing\/invalid or the user doesn\u2019t exist<\/td>\n<\/tr>\n<tr>\n<td>400<\/td>\n<td><code>invalid_points<\/code><\/td>\n<td><code>points<\/code> is <code>&lt;= 0<\/code> or missing<\/td>\n<\/tr>\n<tr>\n<td>401<\/td>\n<td>n\/a<\/td>\n<td>Not authenticated<\/td>\n<\/tr>\n<tr>\n<td>403<\/td>\n<td>n\/a<\/td>\n<td>Authenticated but lacks <code>manage_woocommerce<\/code> capability<\/td>\n<\/tr>\n<tr>\n<td>404<\/td>\n<td>n\/a<\/td>\n<td>Route not registered (plugin disabled or API not enabled)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/section>\n<section>\n<h2>How do I authenticate?<\/h2>\n<ol>\n<li><strong>Application Passwords<\/strong> (recommended for server-to-server): WordPress 5.6+ \u2192 User Profile \u2192 <em>Application Passwords<\/em> \u2192 Create new. Use HTTP Basic Auth (<code>user:application-password<\/code>).<\/li>\n<li><strong>Logged-in cookies + nonce<\/strong> (for in-dashboard calls from wp-admin).<\/li>\n<li><strong>JWT\/OAuth<\/strong> (via separate auth plugins) if your stack uses them.<\/li>\n<\/ol>\n<\/section>\n<section>\n<h2>cURL example (Application Passwords)<\/h2>\n<pre>curl -X POST \"https:\/\/example.com\/wp-json\/wc-reward-points\/v1\/add-points\" \\\r\n  -u \"admin:YOUR-APP-PASSWORD\" \\\r\n  -H \"Content-Type: application\/json\" \\\r\n  -d '{\"user_id\":123,\"points\":250,\"message\":\"Promo: Spring bonus\"}'<\/pre>\n<\/section>\n<section>\n<h2>Postman\/Insomnia quick setup<\/h2>\n<ol>\n<li><strong>Method\/URL:<\/strong> <span class=\"kbd\">POST https:\/\/your-site\/wp-json\/wc-reward-points\/v1\/add-points<\/span><\/li>\n<li><strong>Auth:<\/strong> Basic \u2192 username: your WP user, password: Application Password<\/li>\n<li><strong>Headers:<\/strong> <code class=\"inline\">Content-Type: application\/json<\/code><\/li>\n<li><strong>Body (raw JSON):<\/strong> see example above<\/li>\n<\/ol>\n<\/section>\n<section>\n<h2>PHP example (server-side with Application Passwords)<\/h2>\n<pre>&lt;?php\r\n$url = site_url('\/wp-json\/wc-reward-points\/v1\/add-points');\r\n\r\n$resp = wp_remote_post($url, array(\r\n  'headers' =&gt; array(\r\n    'Authorization' =&gt; 'Basic ' . base64_encode('admin:YOUR-APP-PASSWORD'),\r\n    'Content-Type'  =&gt; 'application\/json',\r\n  ),\r\n  'body' =&gt; wp_json_encode(array(\r\n    'user_id' =&gt; 123,\r\n    'points'  =&gt; 100,\r\n    'message' =&gt; 'Welcome bonus',\r\n  )),\r\n  'timeout' =&gt; 20,\r\n));\r\n\r\nif (is_wp_error($resp)) {\r\n  error_log($resp-&gt;get_error_message());\r\n} else {\r\n  $code = wp_remote_retrieve_response_code($resp);\r\n  $body = json_decode(wp_remote_retrieve_body($resp), true);\r\n  \/\/ Handle $code \/ $body\r\n}<\/pre>\n<\/section>\n<section>\n<h2>WP Nonce + logged-in cookie example (AJAX from wp-admin)<\/h2>\n<pre>fetch('\/wp-json\/wc-reward-points\/v1\/add-points', {\r\n  method: 'POST',\r\n  credentials: 'include', \/\/ send auth cookies\r\n  headers: { 'Content-Type': 'application\/json' },\r\n  body: JSON.stringify({ user_id: 123, points: 50, message: 'Manual adjustment' })\r\n}).then(r =&gt; r.json()).then(console.log);<\/pre>\n<div class=\"note\">The endpoint relies on capability checks (<code>current_user_can('manage_woocommerce')<\/code>), so cookies + a logged-in admin session are sufficient for auth in this context.<\/div>\n<\/section>\n<section>\n<h2>Does this work on multisite?<\/h2>\n<p>Yes. If <em>both<\/em> are true: WordPress is multisite and the plugin setting <code class=\"inline\">multisiteSplitPoints<\/code> is enabled, points are stored under a site-specific meta key:<\/p>\n<pre>woocommerce_reward_points_&lt;BLOG_ID&gt;<\/pre>\n<p>Otherwise, the meta key is:<\/p>\n<pre>woocommerce_reward_points<\/pre>\n<p class=\"small\">This matters if you read totals directly from user meta elsewhere.<\/p>\n<\/section>\n<section>\n<h2>Why might the route be missing (404)?<\/h2>\n<ul>\n<li>The plugin (or its API feature) is disabled.<\/li>\n<li>The plugin option <code class=\"inline\">enable<\/code> is <strong>false<\/strong> (routes register only when enabled).<\/li>\n<li>Your caching layer blocks <code class=\"inline\">\/wp-json<\/code> or POST requests.<\/li>\n<li>A security\/firewall plugin interferes with REST calls.<\/li>\n<\/ul>\n<\/section>\n<section>\n<h2>What happens internally when I call the API?<\/h2>\n<ol>\n<li>Inputs are sanitized and validated.<\/li>\n<li>The code sets <code class=\"inline\">$_POST['userId']<\/code>, <code class=\"inline\">$_POST['points']<\/code>, <code class=\"inline\">$_POST['text']<\/code>, and <code class=\"inline\">$_POST['action']='woocommerce_reward_points_add_points'<\/code>.<\/li>\n<li>It initializes <code class=\"inline\">WooCommerce_Reward_Points_Earning<\/code> and calls <code class=\"inline\">manually_add_user_points()<\/code>.<\/li>\n<li>It reads the updated total from user meta and returns it in the response.<\/li>\n<\/ol>\n<\/section>\n<section>\n<h2>Can I use it to <em>deduct<\/em> points?<\/h2>\n<p>No\u2014this endpoint only <strong>adds<\/strong> points (requires <code class=\"inline\">points &gt; 0<\/code>). To deduct, create a separate endpoint or extend this one with a safe decrement workflow.<\/p>\n<\/section>\n<section>\n<h2>Is there rate limiting or idempotency?<\/h2>\n<p>Not built-in. If you integrate with external systems, implement client-side idempotency (e.g., a unique operation ID) to avoid duplicate awards.<\/p>\n<\/section>\n<section>\n<h2>How can I test quickly?<\/h2>\n<ol>\n<li>Enable the plugin\u2019s API setting (<code class=\"inline\">enable = true<\/code>).<\/li>\n<li>Confirm your user can <code class=\"inline\">manage_woocommerce<\/code>.<\/li>\n<li>Run the cURL example.<\/li>\n<li>Verify the user\u2019s points in the admin or via:\n<pre>get_user_meta(123, 'woocommerce_reward_points', true);<\/pre>\n<p><span class=\"small\">(or the multisite-aware key if applicable)<\/span><\/li>\n<\/ol>\n<\/section>\n<section>\n<h2>Troubleshooting checklist<\/h2>\n<ul>\n<li><strong>401\/403:<\/strong> Not authenticated or lacks capability. Use Application Passwords or an admin session.<\/li>\n<li><strong>404:<\/strong> Ensure the feature is enabled; confirm no firewall blocks <code class=\"inline\">\/wp-json<\/code>.<\/li>\n<li><strong>400 <code class=\"inline\">invalid_user<\/code>:<\/strong> <code class=\"inline\">user_id<\/code> must be an existing WP user.<\/li>\n<li><strong>400 <code class=\"inline\">invalid_points<\/code>:<\/strong> Use a positive integer.<\/li>\n<li><strong>Totals not changing:<\/strong> Check for custom hooks or object caching layers that might roll back\/buffer changes.<\/li>\n<\/ul>\n<\/section>\n<section>\n<h2>Security best practices<\/h2>\n<ul>\n<li>Prefer <strong>Application Passwords<\/strong> for server-to-server calls; restrict to a low-privilege admin account if possible.<\/li>\n<li>Serve over <strong>HTTPS<\/strong> only.<\/li>\n<li>Log each award with a clear, human-readable <code class=\"inline\">message<\/code>.<\/li>\n<li>Consider adding rate limiting or WAF rules if exposing this beyond internal systems.<\/li>\n<\/ul>\n<\/section>\n","protected":false},"author":1,"featured_media":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","product_cat":[],"class_list":["post-20409","faq","type-faq","status-publish","hentry","faq_topics-reward-points"],"_links":{"self":[{"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/faq\/20409","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/faq"}],"about":[{"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/types\/faq"}],"author":[{"embeddable":true,"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/comments?post=20409"}],"version-history":[{"count":2,"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/faq\/20409\/revisions"}],"predecessor-version":[{"id":20411,"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/faq\/20409\/revisions\/20411"}],"wp:attachment":[{"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/media?parent=20409"}],"wp:term":[{"taxonomy":"product_cat","embeddable":true,"href":"https:\/\/www.welaunch.io\/en\/wp-json\/wp\/v2\/product_cat?post=20409"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}