# WooCommerce orders

> Sayproof reading orders from WooCommerce itself; the REST API key, setup, status mapping, the delivery rule and showing reviews on the product page.

Source: /en/integrate/woocommerce/

On a store that runs on WooCommerce, no code needs to be written to get orders to Sayproof. Sayproof connects to the WooCommerce REST API with a REST API key you create and reads new or changed orders itself at regular intervals (every 15 minutes by default). The orders read are recorded in the same way as orders [sent by your store system](/en/integrate/orders.md); the right to write a review and the invitations follow the same rules.

The WordPress and WooCommerce screen names on this page are those of the English admin; if your admin is in another language, the same places appear under their translated names.

## When to use it

- If your store runs on WordPress with WooCommerce, set up the connection on this page.
- If your system has no ready connection, your store system sends the orders: [Sending orders and deliveries](/en/integrate/orders.md).

In the panel the two ways are shown one under the other on the **Integration › Order transfer** tab: **Read from your platform** and **Sent by your store**.

## What you need

| Item | Description |
|---|---|
| Store address | Your WordPress site's address, for example `https://yourbrand.com`. Only the domain is used; a path written after it is dropped. WordPress must therefore be installed at the root of the domain. The site must open over `https`. |
| Consumer key and Consumer secret | A REST API key created for this job with **Read** permission only. |
| Sayproof channel | The channel the orders read are written to. The order's market is this channel's market. |

- In WordPress, **Settings › Permalinks** must be set to an option other than **Plain**; while **Plain** is selected, the REST API addresses do not open.
- A security plugin, firewall or caching layer must not block `/wp-json/` addresses or strip the `Authorization` header.

A Sayproof channel can have one connection per platform.

## Creating the key in WooCommerce

1. In the WordPress admin, open **WooCommerce › Settings › Advanced › REST API** and press **Add key**.
2. Write the key's purpose in the **Description** field (for example "Sayproof").
3. In the **User** field, choose a user who can see orders (**Administrator** or **Shop manager**).
4. Choose **Read** in the **Permissions** field and press **Generate API key**.
5. Copy the **Consumer key** (starts with `ck_`) and **Consumer secret** (starts with `cs_`) values shown. WooCommerce shows the Consumer secret only once, on this screen.

You can revoke the key on the same page whenever you like; reading stops with a revoked key.

## Setting up in the panel

1. On the **Integration › Order transfer** tab, press **Set up a connection** in the **Read from your platform** section.
2. In the **Where to read from** step, choose **WooCommerce** as the platform and the Sayproof channel.
3. In the **Connection details** step, fill in **Store address**, **Consumer key** and **Consumer secret**. **How many days to read back the first time** takes a value between 1 and 365; the default is 30 days.
4. In the **Try it** step, press **Try the connection**. Sayproof connects to WooCommerce and reads the first page (at most 100 orders) of the orders within the chosen number of days. The result shows the number of orders read, the orders that could not be read and at most five sample orders: **Order no.**, **Order date**, **Payment**, **Products**, **Shipping status**, **Has email**. The customer's email address is not shown. Nothing is saved in this step.
5. **Save and start reading** only becomes available after a successful try. If any detail changes after the try, the connection has to be tried again.

After saving, the connection appears on the **Platform connections** card. The card shows **Read frequency**, **Last read**, **In the last read** (orders read and how many were new), **Could not be read** and **Last error**. Buttons:

| Button | Purpose |
|---|---|
| **Read now** | Starts a read without waiting for the next one. It is unavailable while the connection is paused or the channel is off. |
| **Try the connection** | Repeats the try with the saved details; saves nothing. |
| **Pause** / **Resume** | Stops reading or starts it again. |
| **Edit the connection** | Changes the **Store address** and the **Read frequency** (between 5 and 1440 minutes). Reading continues where it left off; orders already transferred are not read again. If the address points to another server, the **Consumer key** and **Consumer secret** are entered again in the same form; the saved key is not sent to the new address (`400 invalid / address_needs_credentials`). The Sayproof channel does not change in this form. |
| **Change the sign-in details** | Saves the new **Consumer secret**; if the **Consumer key** is left empty, the current value is kept. |
| **Remove the connection** | Stops reading. Orders already read and invitations already sent are not deleted. |

## Reading

- A read runs every 15 minutes by default; the frequency can be changed to between 5 and 1440 minutes with **Edit the connection**. The first read takes the orders whose change time falls within the chosen number of days.
- Later reads take the orders changed since 10 minutes before the previous read started. Reading the same order again is safe: the order is updated and no duplicate is created.
- Only one read runs at a time for a connection.
- A read takes at most 50 pages (100 orders per page); a page with fewer than 100 orders counts as the last page. If the limit is reached before the orders run out, the card shows "The last read hit the page limit" and the next read continues from the change time of the last order read (with a 10-minute margin).
- If a read fails, its position does not move; the next read tries the same range again after the read interval and the reason appears on the **Last error** line.

Sayproof reads the order list sorted by change time. The key is sent with HTTP Basic authentication, with the Consumer key as the username and the Consumer secret as the password:

```http
GET https://yourbrand.com/wp-json/wc/v3/orders?modified_after=2026-09-02T12:00:00&dates_are_gmt=true&orderby=modified&order=asc&per_page=100&_fields=id,number,status,date_created_gmt,date_modified_gmt,date_completed_gmt,customer_id,billing.email,billing.phone,line_items,meta_data&page=1
Authorization: Basic <base64(consumer key:consumer secret)>
Accept: application/json
```

- `_fields` asks only for the fields used in the table below.
- All dates are read from the GMT fields.
- Each request times out after 30 seconds and an answer may be at most 20 MB.

## Fields read

Each row lists the fields checked in order; the first one with a value is used.

| Information in Sayproof | Field read in WooCommerce |
|---|---|
| Order number | `number`, `id` |
| Order status | `status` |
| Order date | `date_created_gmt` |
| Change time | `date_modified_gmt` |
| Delivery date | `date_completed_gmt`, `date_modified_gmt` |
| Customer email | `billing.email` |
| Customer phone | `billing.phone` |
| Customer identifier | `customer_id` (empty for guest orders) |
| Language | `wpml_language` in `meta_data` (WPML); otherwise the channel's language |
| Product lines | `line_items` |
| Line identifier | `id` on the line |
| Product code | `sku`, `variation_id`, `product_id` |
| Group code | `product_id` |
| Product name | `name` |
| Quantity | `quantity` |

- The customer email does the same job as the `customer_email` field in the order data sent by a store system: the invitation is sent to this address and the order is verified with it.
- The customer phone does the same job as the `customer_phone` field in the order data: the number is for [invitations on WhatsApp](/en/features/invitations.md#invitations-on-whatsapp). Until invitations by WhatsApp arrive with v2, the phone number is not stored.
- The product code must match the product key in your product source and the code given to the container on the product page.
- The order's address, price and payment details are not stored in Sayproof.

## Status mapping

WooCommerce has no per-line status; the order's `status` applies to the payment and to all of the order's lines. The table also appears in the **How statuses are matched** section of the connection card in the panel.

| WooCommerce code | Meaning in WooCommerce | Payment | Shipping status |
|---|---|---|---|
| `pending` | Pending payment | Payment pending | Placed |
| `on-hold` | On hold (for example, waiting for a bank transfer) | Payment pending | Placed |
| `processing` | Payment received, order being prepared | Paid | Preparing |
| `completed` | Completed | Paid | Delivered |
| `cancelled` | Cancelled | Voided | Cancelled |
| `failed` | Payment failed | Voided | Cancelled |
| `refunded` | Refunded | Refunded | Returned |

- Custom statuses added by plugins (for example `shipped`) are not in the table; orders with such a status are not read and are counted on the **Could not be read** line as "status not recognised".
- Draft orders created on the checkout page (`checkout-draft`), automatic drafts (`auto-draft`) and orders moved to the trash (`trash`) are neither read nor counted.

## Delivery rule

- WooCommerce has no delivery status. Sayproof counts all lines of an order in the `completed` (Completed) status as **Delivered**; this is a platform signal.
- The delivery date is `date_completed_gmt`, or `date_modified_gmt` when that is empty.
- If you set orders to **Completed** when you ship them, set the waiting time on the **Invitation sending** screen to cover the shipping time, so that the invitation does not go out before delivery.
- For invitations and the right to write a review, the line must be **Delivered** and the order **Paid**. The invitation is sent once the waiting time on the **Invitation sending** screen has passed.

## Orders that could not be read

| In the panel | Reason |
|---|---|
| number or date missing | The order has no number, no `date_created_gmt` or no `line_items` list |
| status not recognised | The order's status is not in the table; the order is skipped |
| product code missing on a line | The line has no `id`, or `sku`, `variation_id` and `product_id` are all empty; the line is skipped |
| no readable product lines | None of the order's lines could be read |
| could not be saved in Sayproof | The order was read but could not be saved in Sayproof (for example, there is no rule for the channel's market) |

Orders that could not be read or saved do not stop the read; the other orders are saved. If none of the orders read can be saved, the read counts as failed and its position does not move; the reason appears on the **Last error** line.

## Failures

| In the panel | Meaning |
|---|---|
| the keys needed for the connection are missing | The **Consumer key** or **Consumer secret** is empty |
| the platform did not accept the keys | WooCommerce returned `401`: the key is wrong, revoked or has no **Read** permission; the server may also not be passing the `Authorization` header on |
| the domain could not be reached | The store address could not be resolved |
| the address is not reachable from the internet | The address leads to a private network address; such addresses are not contacted |
| couldn't connect to the server, a secure connection could not be made | The connection or the TLS handshake failed |
| the source did not answer in time | The request got no answer within 30 seconds |
| the address redirects elsewhere; enter the direct address | The site returned a redirect (for example between the address with and without `www`); redirects are not followed |
| the other system returned an error, status code | WooCommerce answered with something other than `200` (for example `http_status: 404` if the REST API is off or **Plain** permalinks are selected, `http_status: 403` if a security layer blocks the request) |
| the platform's answer could not be read, the platform's answer is too large | The answer is not an order list (for example an HTML page) or is larger than 20 MB |
| the stored sign-in details couldn't be read; enter them again | The saved key could not be decrypted; enter it again with **Change the sign-in details** |
| the read stopped with an unexpected error | An unexpected error occurred; the read is tried again after the read interval |

## Showing reviews on the product page

Reviews are shown on the product page with the [script](/en/integrate/script.md). In WooCommerce, the script and the container are placed on the product page with a short piece of code added to your child theme's `functions.php` file or to a code snippets plugin:

```php
add_action( 'woocommerce_after_single_product_summary', function () {
	global $product;
	printf(
		'<div data-review-mount data-review-product="%s" data-review-product-name="%s"></div>',
		esc_attr( $product->get_sku() ?: $product->get_id() ),
		esc_attr( $product->get_name() )
	);
}, 15 );

add_action( 'wp_footer', function () {
	if ( ! is_product() ) {
		return;
	}
	echo '<script type="module" src="https://cdn.sayproof.example/sayproof.js" data-review-site-key="YOUR-SITE-KEY" data-review-base="https://api.sayproof.example" data-review-locale="en"></script>';
} );
```

- The `src` and `data-review-site-key` values are in the **Values for the setup** list on the **Show reviews on the product page** card on the **Integration › Showing reviews** tab in the panel when the **Script (in the browser)** route is chosen.
- The container is added below the product summary and above the related products. To place it elsewhere, choose another WooCommerce hook that your theme uses.
- `data-review-product` is the product's SKU; when the SKU is empty, the product number is used. For simple products this matches the product code Sayproof reads from the order.
- For variable products, the order carries the variation's SKU, while `$product->get_sku()` on the product page gives the parent product's SKU. For such products, to show the reviews of all variations together, add `data-review-scope="group"` and set `data-review-product` to `$product->get_id()`; the group code is the product number in WooCommerce.

## Domain verification

Reviews only appear on pages of verified domains. Add the domain in the panel and verify it: [Domain verification](/en/integrate/channels-and-keys.md#domain-verification). In WordPress, the verification file is placed at the site root with the server's file manager or over FTP; if you can't place a file, choose **DNS record** as the **Verification method** and add a `TXT` record in your domain's DNS settings.

## Security

- The Consumer secret is stored encrypted and never shown again in the panel or in API answers.
- Only `https` addresses are contacted; private network addresses and redirects are refused.
- The key should only have **Read** permission; Sayproof changes nothing in WooCommerce. To cut the connection completely, revoke the key in WooCommerce.
- Connections are only seen and managed by a user signed in to the panel with the "Can change settings" permission; these actions cannot be taken with an API key.
- Setting up, changing, trying, reading manually and removing are recorded on the **Activity history** screen.
