Claims
Claims — Create a claim (V1)
Create a protection claim with one JSON request, the same way the return
app and merchant portal do. Photos are required for Damaged and
Tracking Not Updated. Other reasons may omit claimed_images. If you have
photos, upload them first, then put the returned URLs on the claim.
Malware scanning of uploads is V2. V1 still checks JWT, file type, and size.
Base URLs
| Environment | URL |
|---|---|
| Production | https://api.protectplus.io |
| Local | http://localhost:8088 (or your configured PORT) |
Production flow
POST /auth/v1/token— get a Bearer JWT- Optional:
POST /claims/uploadsonce per file — get an https URL POST /claims— JSON body + requiredIdempotency-Key(UUID)
store_id comes from the JWT. Customer name and email are taken from the
order — do not send them.
Upload an image (required for Damaged / Tracking Not Updated)
POST /claims/uploads
Content-Type: multipart/form-data
Field name: file
Allowed: jpeg, png, webp, gif, heic, mp4, mov (default max 10 MB)
curl -X POST "https://api.protectplus.io/claims/uploads" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@./photo.jpg"Response 201:
{
"data": {
"url": "https://storage.googleapis.com/...",
"type": "image",
"mime_type": "image/jpeg"
}
}type is image, gif, or video so the frontend can pick <img> vs <video>.
Create the claim
POST /claims
Content-Type: application/json
Idempotency-Key: <uuid> (required)
| Field | Required | Description |
|---|---|---|
order_reference | Yes | Human order number (store or Protect+ number) |
claimed_type | Yes | reship or refund |
claims_full_order | Yes | true = entire order; false = selected lines |
claims_items | Unless full-order | Each item needs line_id and quantity |
claim_reason | No | Exact string only: Lost, Stolen, Damaged, Wrong Product, Tracking Not Updated. Anything else returns 422. Same as the portal chips. Damaged and Tracking Not Updated also require photos (EVIDENCE_REQUIRED). |
additional_comment | No | Portal More details. One note for all lines. Copied onto every item unless a line sends its own. |
claims_items[].additional_comment | No | More details for that line only. Overrides the top-level note on that line. |
claims_items[].claimed_images | If that line is Damaged / Tracking Not Updated (unless top-level photos) | https URLs from /claims/uploads for that line |
claimed_images | If full-order Damaged / Tracking Not Updated | Same URLs applied to every line on a full-order claim |
shipping_address | No | Reship destination |
Do not send customer_email or first_name.
cURL example (with photos)
curl -X POST "https://api.protectplus.io/claims" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"order_reference": "PP-100234",
"claimed_type": "refund",
"claims_full_order": false,
"claim_reason": "Damaged",
"claims_items": [
{
"line_id": "111",
"quantity": 1,
"claimed_images": ["https://storage.googleapis.com/bucket/photo.jpg"]
}
]
}'Damaged / Tracking Not Updated without photos is 422 EVIDENCE_REQUIRED.
Omit claimed_images only for Lost, Stolen, Wrong Product, or no reason. Success is 201.
Full order — one More details for all
{
"order_reference": "PP-100234",
"claimed_type": "refund",
"claims_full_order": true,
"claim_reason": "Damaged",
"additional_comment": "Box was crushed on arrival.",
"claimed_images": ["https://storage.googleapis.com/bucket/photo.jpg"]
}Specific — More details per line (or one for all)
{
"order_reference": "PP-100234",
"claimed_type": "refund",
"claims_full_order": false,
"claim_reason": "Damaged",
"additional_comment": "Used if a line has no note of its own.",
"claimed_images": ["https://storage.googleapis.com/bucket/shared.jpg"],
"claims_items": [
{
"line_id": "111",
"quantity": 1,
"additional_comment": "Corner of this item is cracked.",
"claimed_images": ["https://storage.googleapis.com/bucket/item111.jpg"]
},
{
"line_id": "222",
"quantity": 1
}
]
}Line 111 stores the per-line note and its own photo. Line 222 gets the top-level additional_comment and the top-level claimed_images. The claim row also stores the top-level note.
Error handling (partner side)
On failure the API does not create the claim. You receive RFC 7807 Problem
Details with a stable code:
{
"type": "https://api.protectplus.io/errors/claim-already-exists",
"status": 409,
"detail": "A claim already exists for this order.",
"code": "CLAIM_EXISTS"
}Handle status + code (CLAIM_EXISTS, NOT_PROTECTED, REFUND_NOT_ELIGIBLE,
RETURNS_OVERLAP, STOCK_INSUFFICIENT, INVALID_REASON, EVIDENCE_REQUIRED,
VALIDATION_ERROR, UPLOAD_FAILED, CLAIM_CREATE_FAILED).
How a claim payout is priced
When a claim is approved, Get Plus prices it from the order you sent to the
Orders API. When the order's price figures are all sent and agree, the payout
is what was paid for the claimed items: each line's line_total, less its
share of any order-wide discount. When they don't, the order is flagged, and
the claim pays the claimed items' share of the subtotal instead (the
fallback).
Send every price figure
The rule needs every price figure, each agreeing with the others. Send a zero
as 0: 0 means zero everywhere, so a line_total of 0 is a free line and
a discount_amount of 0 means the line had no discount. A figure you leave
out is read as not sent, never as zero.
| Field | What it is |
|---|---|
line_items[].unit_price | One unit, before any discount. |
line_items[].quantity | Units ordered. |
line_items[].line_total | What the customer paid for the whole line after the line's own discount, before any order-wide discount. It is the figure a claim pays a line from. A line total up to a cent above the line's unit_price × quantity is read as the price. More than that gets the order flagged. |
line_items[].discount_amount, line_items[].discount_percent | Optional. The line's own discount for all its units together, as an amount or as a percentage of unit_price × quantity. They explain line_total and must agree with it. |
amounts.subtotal | The items after all discounts, line and order-wide, the way Shopify defines it. It excludes shipping and the protection fee, and includes tax only when prices include tax. |
amounts.discount_total | Every discount on the order: every line's unit_price × quantity, less subtotal. |
discounts[] | One entry per code or discount, with its amount and applies_to: line for a discount already taken off its lines' line_total, order for an order-wide discount. |
amounts.shipping_total, amounts.tax_total (or tax_lines[]), amounts.protection_cost | As charged. |
amounts.grand_total | subtotal + shipping_total + protection_cost, plus tax_total only when prices exclude tax. Whether prices include tax is read from this sum. |
discount_amount, discount_percent, discount_total, discounts[] and
grand_total are not used to pay a claim; each must agree with the other
figures, or the order is flagged. shipping_total and tax_total are used
only for the shares in step 4 below. The API reference for
PUT /orders/{order_number} has the full definitions, and the figures an
order can't have at all, which are refused with 422.
The rule
When every figure is sent and they agree:
-
Each line was paid its
line_total, less its share of the order-wide
discount. -
The order-wide discount is whatever the line totals come to above
subtotal. It is shared across all the lines in proportion to their
line totals, and the shares add up to the cent. -
A claimed line pays what it was paid, for the units claimed: units claimed
÷quantity, rounded half up to the cent.quantityis the units ordered,
so a partial cancellation doesn't change what a unit was paid. Cancelled
units can't be claimed. -
When your store is set up with Get Plus to refund shipping on claims, the
claim's share ofshipping_totalis added. Tax follows its own setting,
the way Shopify refunds it:- When prices exclude tax, the claim's share of
tax_totalis added if your
store is set up to refund tax on claims. Otherwise nothing is added. - When prices include tax, the claim's share of
tax_totalis taken out if
your store is not set up to refund tax on claims. If it is, the claim pays
each item's price as it is, tax included.
The claim's share of
shipping_totalortax_totalis that figure × what
the claimed items were paid ÷ what all the items were paid, rounded half up
to the cent. Whether prices include tax is read from your figures:
grand_totaladdstax_totalonly when prices exclude it. - When prices exclude tax, the claim's share of
A claim that comes to 0.00 is refused, whether it asks for a refund or a
reship: a claim on a free line, for example. A claim on an item that isn't on
the order is refused too; the fallback doesn't price it either.
The fallback
The claim pays the claimed items' share of the subtotal instead when the
order is flagged. An order is flagged when:
- a line has no
line_total; - another price figure wasn't sent:
shipping_total,discount_total,
grand_total, or the tax (tax_total, ortax_lineswith amounts). Send
0when there was none; - the figures don't agree, beyond a cent per line of rounding. That includes a
line discount listed indiscountsbut left off its line, and a
discount_totalof0on an order that had a discount; - the figures show something else a claim can't be priced from: the same line
sent twice (samesku, price, quantity and totals), aline_totalmore than
a cent above the line'sunit_price×quantity, asubtotalthat has the
shipping in it, the protection fee sent as a line item, or a cancelled
line's figures changed, or the subtotal moved without the lines (neither the
discount's amount nor its share held), after a partial cancellation (see
below).
The fallback also prices the claim when your store is set up to refund tax on
claims and the tax is so small, a cent or two per line, that the figures can't
show whether prices include it.
The share of the subtotal is subtotal × the claimed items at unit_price ÷
every item on the order at unit_price, rounded half up to the cent. It never
pays more than the claimed items' list price, and it adds no shipping and no
tax. When the grand total shows that prices include tax and your store is not set up to refund tax on claims, the same share of tax_total is taken out of it.
A claim on a flagged order is shown to staff, with the reason, before it is
approved.
After a partial cancellation
Cancel units with POST /orders/{order_number}/cancel. The cancellation is
recorded on its own, so don't take the cancelled units out of any figure: keep
sending each cancelled line's quantity, unit_price and line_total as
ordered, and leave the cancelled units in subtotal, discount_total and
grand_total. A claim on the units left pays what each unit was paid when it
was ordered.
A later PUT that changes a cancelled line's quantity, unit_price or
line_total restates the line for the units left. So does one that moves
subtotal without the lines, such as a PUT that sends every line as ordered
but lowers subtotal, discount_total and grand_total by the cancelled
units: read as an order-wide discount, it would be shared over every line, the
ones nothing was cancelled on included. Either reads just as well as a discount
on every unit ordered, and nothing says which you meant, so the order is
flagged and its claims fall back to the share of the subtotal.
Get Plus keeps each line's figures as they stood when units were first
cancelled on it, and the order's discount as it stood at its first partial
cancellation: what the line totals come to above subtotal, or, when the lines
carry no line_total, what their unit_price × quantity come to above it.
What it keeps is the figures at that cancellation, so send any correction to
them before you cancel. The discount is kept, not the totals, so a PUT can
still add, remove or re-price items that nothing was cancelled on, refund
shipping, or send a figure for the first time: move the lines and subtotal
together and the discount stays as it was. It is kept as an amount or as a
share of the items. A percentage discount on the whole order grows and shrinks
with the items, so it keeps its share when items are added, removed or
re-priced after a cancellation, and the order is not flagged, though the amount
off has changed. The discount gets the order flagged only when it keeps neither
its amount nor its share, beyond rounding (a cent a line, grown with the items). On an order whose subtotal has
the shipping in it and whose lines carry no line_total, a shipping refund
after a cancellation lowers subtotal alone: it looks like a restated
subtotal, and the order is flagged. Keep the shipping out of subtotal and
send it as shipping_total, and send line totals, to avoid it. An order
restated after a cancellation stays flagged on every later write until its
cancelled lines and its discount are as they were. Sent that way again, it is
no longer flagged.
That holds for each line and for the order on its own: for a line whose units
were first cancelled after Get Plus began keeping these figures, and for an
order whose first partial cancellation came after then. A line or an order
cancelled before then has none kept, so a request that changes it is compared
only with the order as it was stored before that request: that request is
flagged, but the same figures sent again are not, and a claim is then paid from
the changed figures. On the ring at 10.00 × 3 below, the two rings left would
then be paid 13.33 rather than the 20.00 they were ordered at. On an order
first partially cancelled before then, a line first cancelled since keeps its
figures, and stays flagged until they are as they were. Keep sending the
figures as ordered on those orders too.
Worked examples
Every figure is sent, with shipping and tax at 0 and no protection fee unless
stated.
| Order | Claim | Pays |
|---|---|---|
A at 50.00 with discount_amount 25.00 and line_total 25.00; B at 50.00 with line_total 50.00. subtotal 75.00, discount_total 25.00, grand_total 75.00 | A | 25.00 |
| The same order | B | 50.00 |
A and B at 50.00 each, both with line_total 50.00, and 25.00 off the whole order, listed in discounts as order. subtotal 75.00, discount_total 25.00, grand_total 75.00 | A or B | 37.50 |
In the first order the discount was on A, so A pays what was paid for it and B
pays its full price. In the second, the 25.00 came off the whole order, so A
and B carry half of it each.
With tax. Say the first order's prices include 6.00 of tax: tax_total is
6.00, and grand_total stays 75.00, because the tax is already inside the
75.00 subtotal. If your store is not set up to refund tax on claims, A pays
25.00 less its share of the tax, 2.00 (A was paid 25.00 of the 75.00): 23.00.
B pays 50.00 less 4.00: 46.00. If your store is set up to refund tax on
claims, A pays 25.00 and B 50.00. Had the prices excluded the tax,
grand_total would be 81.00, and a store not set up to refund tax on claims
would pay A 25.00 and B 50.00: there is no tax in the prices to take out.
Rounding. A ring at 79.99 with discount_percent 30, line_total 56.00,
subtotal 56.00, discount_total 23.99 and grand_total 56.00. 30% of 79.99
is 23.997, so 24.00 off leaves 55.99, a cent from the 56.00 sent: a cent per
line is taken as rounding, and the figures agree. A claim on the ring pays
56.00, because line_total is the figure a claim pays from.
A free line. A at 50.00 with line_total 50.00, and a gift at 25.00 with
line_total 0. subtotal 50.00, discount_total 25.00, grand_total 50.00.
A claim on A pays 50.00. A claim on the gift comes to 0.00, so it is refused,
as a refund or as a reship.
A partial cancellation.
| Order | Claim | Pays |
|---|---|---|
Three rings at 79.99, one of them free, so line_total 159.98, and a pendant at 49.99 with line_total 49.99. subtotal 209.97, discount_total 79.99, grand_total 209.97 | One ring | 53.33 |
| The same order | The pendant | 49.99 |
| The same order after one ring is cancelled, still sent as ordered | One ring | 53.33 |
A ring at 10.00 × 3 with line_total 30.00. subtotal 30.00, discount_total 0, grand_total 30.00. One ring is cancelled and the order is re-sent unchanged | The 2 rings left | 20.00 |
The same order re-sent after the cancellation with line_total 20.00, subtotal 20.00, discount_total 10.00 and grand_total 20.00 | The 2 rings left | 13.33 |
The same order re-sent after the cancellation with the ring as ordered, line_total 30.00, but subtotal 20.00, discount_total 10.00 and grand_total 20.00 | The 2 rings left | 13.33 |
A ring at 10.00 × 3 with line_total 30.00, and a pendant at 50.00 with line_total 50.00. subtotal 80.00, discount_total 0, grand_total 80.00. One ring is cancelled, and the order is re-sent with both lines as ordered but subtotal 70.00, discount_total 10.00 and grand_total 70.00 | The pendant | 43.75 |
| The same | The 2 rings left | 17.50 |
The same order re-sent as ordered again: subtotal 80.00, discount_total 0, grand_total 80.00 | The pendant, or the 2 rings left | 50.00, or 20.00 |
A at 50.00 with discount_amount 25.00 and line_total 25.00, and B at 25.00 × 2 with line_total 50.00. subtotal 75.00, discount_total 25.00, grand_total 75.00. One B is cancelled, and the order is re-sent with C at 20.00, line_total 20.00, added: subtotal 95.00, discount_total 25.00, grand_total 95.00 | A, the B left, or C | 25.00, 25.00, or 20.00 |
The same A and B with 10% off the whole order: subtotal 67.50, discount_total 32.50, grand_total 67.50. One B is cancelled, and the order is re-sent with C at 20.00, line_total 20.00, added, still 10% off: subtotal 85.50, discount_total 34.50, grand_total 85.50 | A, the B left, or C | 22.50, 22.50, or 18.00 |
The same 10%-off order re-sent after the cancellation with A and B as ordered, but subtotal 45.00, discount_total 55.00 and grand_total 45.00 | A, or the B left | 22.50, or 11.25 |
Each of the three rings was paid 159.98 ÷ 3 = 53.33, and cancelling one of them
doesn't change what the others were paid. In the fifth row the ring was
restated for the two units left, with the subtotal moved to match. In the sixth
the ring was sent as ordered and only the subtotal was lowered: a 10.00
discount the order didn't have at its cancellation. Either way the order is
flagged and the claim is paid the share of the subtotal: 20.00 × 20.00 ÷ 30.00
= 13.33. Sent as ordered, the same claim pays 20.00.
The ring and pendant order is re-sent with its lines as ordered, but with its
subtotal lowered by the cancelled ring, so it is flagged too, and each claim is
paid its share of the 70.00 subtotal: 70.00 × 50.00 ÷ 80.00 = 43.75 for the
pendant, and 70.00 × 20.00 ÷ 80.00 = 17.50 for the two rings. It stays flagged
when the same figures are sent again. Re-sent as ordered, it is no longer
flagged, and the claims pay what was paid for the items: 50.00 and 20.00.
The A, B and C order adds an item after the cancellation, and its subtotal
moves up with it. Its line totals come to its subtotal before and after, so its
discount is what it was at the cancellation, and it is not flagged: each claim
pays what was paid for the item. An order whose lines carry no line_total is
held the same way, by what its unit_price × quantity come to above
subtotal. Sent with the subtotal lowered by a cancelled unit, it is flagged
too, although its claims are paid the share of the subtotal either way.
The 10%-off order adds C the same way. Its order-wide discount moves from 7.50
to 9.50 with the items, but keeps its share: subtotal is 90% of the line
totals before and after. So it is not flagged, and each claim pays what was
paid for the item: A 22.50 (its 25.00 less 10%), the B left 22.50, and C 18.00.
Removing an item that nothing was cancelled on works the same way. Re-sent
instead with its lines as ordered but its subtotal lowered by the cancelled B's
22.50, its discount keeps neither its amount nor its share, so it is flagged,
and each claim is paid its share of the 45.00 subtotal: 45.00 × 50.00 ÷ 100.00
= 22.50 for A, and 11.25 for the B left.
The fallback. Take an order with discounts: two Red Widgets at 20.00 with
a 4.00 sale on their line, a Blue Gadget at 15.00, and SPRING10 taking 5.10
off the order. Its prices exclude tax. These are its price fields:
{
"amounts": {
"subtotal": 45.90,
"discount_total": 9.10,
"shipping_total": 5.00,
"tax_total": 3.67,
"protection_cost": 1.99,
"grand_total": 56.56
},
"discounts": [
{ "type": "sale", "amount": 4.00, "applies_to": "line" },
{ "code": "SPRING10", "type": "percentage", "amount": 5.10, "applies_to": "order" }
],
"line_items": [
{ "sku": "SKU-A", "name": "Red Widget", "quantity": 2, "unit_price": 20.00, "discount_amount": 4.00, "line_total": 36.00 },
{ "sku": "SKU-B", "name": "Blue Gadget", "quantity": 1, "unit_price": 15.00, "discount_amount": 0.00, "line_total": 15.00 }
]
}Sent as shown, a claim pays 16.20 for one Red Widget and 13.50 for the Blue
Gadget. Sent any of these ways, it is flagged, and a claim pays the share of
the 45.90 subtotal instead: 16.69 for one Red Widget (45.90 × 20.00 ÷ 55.00)
and 12.52 for the Blue Gadget.
| The same order, but | Why it is flagged |
|---|---|
The order entry in discounts is 16.00 | The order entries come to 16.00, but the line totals come to only 5.10 above subtotal |
The sale is only in discounts: the Red Widgets' discount_amount is 0 and their line_total 40.00 | The line entry lists 4.00 of discounts that no line carries, and the order entry doesn't match the 9.10 the line totals now come to above subtotal |
The Red Widgets' line_total is 38.00 | 40.00 less their 4.00 discount_amount is 36.00, not 38.00 |
It has no discount_total | A figure wasn't sent |
Sending 0 for a figure that wasn't zero is flagged the same way. The
three-ring order above, sent with discount_total 0, is flagged, since the
rings had 79.99 off, and a claim on the pendant pays 36.20, its share of the
subtotal, rather than 49.99.
An order that sends only unit_price, quantity, subtotal and grand_total
is flagged too, since it has no line totals, shipping, discount total or tax:
its claims are all paid the share of the subtotal. So are claims on an order
that sends the same line twice, or has the shipping inside subtotal.
Updated 13 days ago