Consignify API v1
RESTful API for managing consignors, products and payouts in your Shopify store.
All requests require a Bearer token generated in the Consignify app settings. Include it in the Authorization header:
Authorization: Bearer <api_key>https://consignify.shop/api/v1Consignors
List the shop's consignors that aren't archived. The list isn't paginated.
[
{ "id": "w13v…", "givenName": "Ludwig", "surname": "van Beethoven", "key": "Beethoven" },
{ "id": "vr0z…", "givenName": "Galileo", "surname": "Galilei", "key": "Galileo" }
]Consignor fields
POST /consignors and PATCH /consignor/{id} accept these fields. When you create a consignor, the fields you leave out are empty or use the shop default, and archived is false. When you update one, the fields you leave out keep their values.
key: your unique code for the consignor. Required when creating.givenName,surname,notes: text, ornullto clear.email: an email address no other consignor in the shop uses, ornullto clear.phone: a phone number with at least 5 digits, ornullto clear.share: the consignor's share as a percentage (0–100).nullclears it, so the shop default applies. A product's own share override takes precedence.unsoldItemFate:SHOP_DEFAULT,DONATEorRETURN.archived:trueorfalse. Archiving a consignor who has active products returns 409.portalEnabled,productEntryEnabled,activeProductEditEnabled:SHOP_DEFAULT,ENABLEDorDISABLED. They control portal access, adding products from the portal, and editing active products from the portal.productLocationId: the Shopify location where the consignor manages inventory in the portal, asgid://shopify/Location/N.null, an empty string orSHOP_DEFAULTuses the shop default.merchantAbsorbsDiscount:trueto calculate this consignor's commission on the price before discounts,falseto use the discounted price, ornullfor the shop default.locale: the language for emails sent to the consignor, as a language tag such asfrorde-CH(up to 10 characters).nulluses the shop's language.
Create a consignor with the fields above. Only key is required. Responds 201 with the new consignor. A key or email that another consignor already uses returns 400.
{
"key": "test-123",
"email": "friend@example.com",
"unsoldItemFate": "DONATE",
"merchantAbsorbsDiscount": false
}{
"id": "c804crn7lgesjc1fshf3b0bb",
"key": "test-123",
"shopId": "efrus-test-store3.myshopify.com",
"givenName": null,
"surname": null,
"share": null,
"email": "friend@example.com",
"phone": null,
"notes": null,
"unsoldItemFate": "DONATE",
"createdAt": "2025-07-31T16:42:00.000Z",
"updatedAt": "2025-07-31T16:42:00.000Z",
"archived": false,
"portalEnabled": "SHOP_DEFAULT",
"productEntryEnabled": "SHOP_DEFAULT",
"activeProductEditEnabled": "SHOP_DEFAULT",
"productLocationId": null,
"merchantAbsorbsDiscount": false,
"locale": null
}Get a single consignor by id.
{
"id": "w13vmscy6j5uub1taivtmp5i",
"key": "Beethoven",
"shopId": "efrus-test-store3.myshopify.com",
"givenName": "Docs",
"surname": "Sample",
"share": null,
"email": "hello+beethoven@consignify.shop",
"phone": null,
"notes": null,
"unsoldItemFate": "DONATE",
"createdAt": "2025-07-31T15:53:56.499Z",
"updatedAt": "2025-07-31T16:40:30.883Z",
"archived": false,
"portalEnabled": "SHOP_DEFAULT",
"productEntryEnabled": "SHOP_DEFAULT",
"activeProductEditEnabled": "SHOP_DEFAULT",
"productLocationId": null,
"merchantAbsorbsDiscount": null,
"locale": null
}Update a consignor with the fields above. Only the fields you send change. Archiving a consignor who still has active products returns 409.
{ "givenName": "Docs", "surname": "Sample" }{
"id": "w13v…",
"key": "Beethoven",
"shopId": "efrus-test-store3.myshopify.com",
"givenName": "Docs",
"surname": "Sample",
"share": null,
"email": "hello+beethoven@consignify.shop",
"phone": null,
"notes": null,
"unsoldItemFate": "DONATE",
"createdAt": "2025-07-31T15:53:56.499Z",
"updatedAt": "2025-07-31T16:40:30.883Z",
"archived": false,
"portalEnabled": "SHOP_DEFAULT",
"productEntryEnabled": "SHOP_DEFAULT",
"activeProductEditEnabled": "SHOP_DEFAULT",
"productLocationId": null,
"merchantAbsorbsDiscount": null,
"locale": null
}All products associated with this consignor.
[
{
"id": "14985358311805",
"title": "The 3p Fulfilled Snowboard",
"status": "ACTIVE",
"totalInventory": 19,
"consignerShare": null,
"shopifyUpdatedAt": "2025-07-28T17:58:04.000Z"
},
{
"id": "14985358082429",
"title": "The Videographer Snowboard",
"status": "ACTIVE",
"totalInventory": 46,
"consignerShare": null,
"shopifyUpdatedAt": "2025-07-28T17:58:05.000Z"
}
]Assign products to this consignor. Send an array of numeric Shopify product ids (numbers or numeric strings, not GIDs). An invalid body or an archived consignor returns 400, and an unknown consignor 404. Otherwise the response is always 202 and streams plain-text progress lines. A product that fails appears only as an Error processing product line, and the last line reads Completed with N errors or All products processed successfully.
[ 14985358082429, 14985358016893 ]Started processing 2 products...
Processed product 14985358082429 (1/2)
Error processing product 14985358016893: Product not found on Shopify: 14985358016893
Completed with 1 errorsList this consignor's payouts, paid and unpaid (an unpaid payout has paidAt null). An unknown consignor id returns an empty list.
[
{
"id": "ovfjhrqu1e6on4eta4wqq6sw",
"shopId": "efrus-test-store3.myshopify.com",
"reference": "#R1001",
"amount": 1067.94,
"adjustment": 0,
"currencyCode": "GBP",
"paidAt": "2025-07-31T15:55:01.660Z",
"createdAt": "2025-07-31T15:55:01.662Z",
"note": "",
"updatedAt": "2025-07-31T15:55:03.624Z",
"consignerId": "w13vmscy6j5uub1taivtmp5i",
"receiptSentAt": "2025-07-31T15:55:03.623Z",
"receiptStatus": "SENT",
"receiptStatusReason": null
}
]Products
Paginated list of products.
{
"data": [ {
"id": "14985357918589",
"updatedAt": "2025-07-31T15:54:23.392Z",
"shopId": "efrus-test-store3.myshopify.com",
"consignerId": null,
"shopifyUpdatedAt": "2025-07-28T17:58:05.000Z",
"consignerShare": null,
"status": "ACTIVE",
"totalInventory": 8,
"title": "The Compare at Price Snowboard"
},...],
"nextCursor": "14985…"
}Single product details.
{
"id": "14985357918589",
"updatedAt": "2025-07-31T15:54:23.392Z",
"shopId": "efrus-test-store3.myshopify.com",
"consignerId": null,
"shopifyUpdatedAt": "2025-07-28T17:58:05.000Z",
"consignerShare": null,
"status": "ACTIVE",
"totalInventory": 8,
"title": "The Compare at Price Snowboard"
}The consignor assigned to this product. Returns null when the product has no consignor or isn't found.
null | { id, key, givenName, … }Assign the product to a consignor by key or id. Send { "key": null } or { "id": null } to remove its consignor. Responds 201 with an empty body. Errors: 400 for an invalid product id or body, or an archived consignor; 404 when the consignor isn't found, or when assigning a product Shopify doesn't have; 500 when the update fails.
{ "key": "Beethoven" }HTTP 201 (empty body)The product's own share override, as a percentage. null means the product has no override and uses the consignor's share or the shop default.
{ "share": 50 }Set the product's share override (0–100). Send { "share": null } to remove it, so the consignor's share or the shop default applies. Responds 201 with an empty body. Errors: 400 for an invalid product id or body; 404 when setting a share on a product Shopify doesn't have; 500 when the update fails.
{ "share": 45 }HTTP 201 (empty body)Set the consignor, the share override, or both on up to 250 products in one request. Responds 200 with the number of products and an entry for each product that wasn't updated; retryable is true when sending that product again can succeed. Errors: 400 for an invalid body or an archived consignor; 404 for an unknown consignor; 413 for more than 250 products.
ids: numeric Shopify product ids (numbers or numeric strings), at least one and at most 250. Duplicates count once.consignerId: a consignor id, ornullto remove the products' consignor.share: a number from 0 to 100, ornullto remove the products' share override. SendconsignerId,shareor both; a field you leave out is unchanged.
{
"ids": [ 14985358082429, 14985358016893 ],
"consignerId": "w13vmscy6j5uub1taivtmp5i",
"share": 45
}{
"total": 2,
"failed": [
{
"id": "14985358016893",
"error": "This shop is busy with another change. Please try again in a moment.",
"retryable": true
}
]
}Paginated order line-items for this product.
{
"data": [ {
"id": "34838143369597",
"orderId": "11725356106109",
"productId": "14985357918589",
"variantId": "55263182029181",
"sku": null,
"originalQuantity": 1,
"currentQuantity": 1,
"unitPrice": 785.95,
"unitPriceDiscounted": 785.95,
"currencyCode": "GBP",
"isFulfilled": true,
"fulfillmentCreatedAt": "2025-03-04T11:24:56.000Z",
"fulfillmentLastUpdatedAt": "2025-03-04T11:24:56.000Z",
"itemPayout": null
}, ...],
"nextCursor": null }Payout line-items for this product.
[] | [ { id, commissionRate, commissionRateType, quantity, amount, payout: { id, reference } } ]Payouts
Paginated list of the shop's payouts, sorted by reference as text (so #R1000 comes before #R999). The cursor is a payout reference: pass the previous page's nextCursor. References start with #, so URL-encode the cursor (cursor=%23R1001).
{ "data": [ { "id": "ovfj…", "reference": "#R1001", … } ], "nextCursor": null }Create a payout for a consignor. The payout amount and each line's amount are stored as you send them and aren't recalculated, so send values that add up. The payout is marked paid now unless you send "unpaid": true. Responds 201 with the payout, its sale lines (payoutLineItems) and its additional items (customLineItems).
consignerId: the consignor's id. Required.amount: the payout total. Required.lineItems: the sales to pay out, in the format below. Required; may be empty. A sale can be in only one payout.customLineItems: additional items, each{ "description": "…", "amount": 10 }. Both are required; use a negative amount for a deduction such as a fee. Optional.note: an optional note.adjustment: an optional adjustment amount, ornullfor none. It's stored as sent; include it inamountyourself.unpaid:trueto create the payout unpaid. Defaults to false.sendReceipt: accepted but ignored. The API doesn't email receipts; you can send one from the payout in the Consignify admin.
id: the sale's order line item id, as returned by/product/{id}/items.quantity: the number of units paid out, a whole number of at least 1.commissionRateType:PERCENT,FIXED,PERCENT_OF_NETorPERCENT_MINUS_COST.commissionRate: a percentage from 0 to 100, or an amount forFIXED.amount: what the consignor is paid for this line, 0 or more.fulfilledAt: when the sale was fulfilled, as an ISO 8601 date and time.salesTotalAtPayout,salesTotalBeforeDiscountAtPayout: the sale's total after and before discounts.costBasis: required forPERCENT_OF_NETandPERCENT_MINUS_COST, otherwise optional. The net price forPERCENT_OF_NET, the total cost forPERCENT_MINUS_COST.
Errors: 400 for an invalid body (JSON errors); 404 for an unknown consignor; 409 when a sale is already in another payout, belongs to another consignor or none, was marked settled, or no longer exists; 503 when the shop is busy.
{
"consignerId": "w13vmscy6j5uub1taivtmp5i",
"amount": 466.57,
"lineItems": [
{
"id": "34838143369597",
"quantity": 1,
"commissionRate": 60,
"commissionRateType": "PERCENT",
"amount": 471.57,
"fulfilledAt": "2025-03-04T11:24:56.000Z",
"salesTotalAtPayout": 785.95,
"salesTotalBeforeDiscountAtPayout": 785.95
}
],
"customLineItems": [ { "description": "Cleaning fee", "amount": -5 } ],
"note": "Bank transfer",
"unpaid": true
}{ "id": "lsl…", "reference": "#R1003", "amount": 466.57, "adjustment": null, "paidAt": null, "note": "Bank transfer", "payoutLineItems": [ { "id": "34838143369597", "quantity": 1, "commissionRate": 60, "commissionRateType": "PERCENT", "amount": 471.57, …, "orderLineItem": { … } } ], "customLineItems": [ { "id": "…", "description": "Cleaning fee", "amount": -5, … } ], … }Get payout details including line items.
{ "id": "lsl…", "reference": "#R1003", "payoutLineItems": [ { "id": "…", "amount": 0, "orderLineItem": { … } } ], "customLineItems": [ { "id": "…", "description": "Bonus", "amount": 10 } ] }Replace the content of an unpaid payout. The body takes the same fields as creating one, without consignerId, and replaces the payout as a whole. Paid payouts can't be edited (409).
lineItemsis required and lists every sale the payout should contain; a sale you leave out is removed. Each entry needs all the line fields. A sale already in the payout keeps its saved line whatever you send for it, so to change a line, remove the sale and add it again. Only added sales take your values and must meet the line rules.- Leaving out
customLineItemsdeletes all the payout's additional items. - The
amountis recalculated from the lines, additional items and adjustment; anamountyou send is ignored. Leaving outadjustmentkeeps the current one, andnullclears it. notemust be text:nullis rejected. Leaving it out keeps the current note. To clear the note, use PATCH.- The payout is marked paid unless you send
"unpaid": true.sendReceiptis ignored. Errors are as for creating a payout, plus 404 for an unknown payout and 409 for a paid one.
{
"lineItems": [ { "id": "34838143369597", "quantity": 1, … } ],
"customLineItems": [ { "description": "Bonus", "amount": 10 } ],
"note": "Adjusted total",
"adjustment": 0,
"unpaid": true
}{ "id": "lsl…", "reference": "#R1003", "amount": 481.57, "adjustment": 0, "paidAt": null, "note": "Adjusted total", "payoutLineItems": [ { "id": "34838143369597", "amount": 471.57, …, "orderLineItem": { … } } ], "customLineItems": [ { "id": "…", "description": "Bonus", "amount": 10, … } ], … }Partially update a payout. "note" works on both paid and unpaid payouts (send an empty string or null to clear it). "paid": true marks an unpaid payout as paid; sending it on an already-paid payout returns 409.
{ "note": "Paid via bank transfer", "paid": true }{ "id": "lsl…", "reference": "#R1003", "note": "Paid via bank transfer", "paidAt": "2025-07-31T…", "payoutLineItems": [ … ], "customLineItems": [ … ] }Delete payout.
HTTP 204 (no content)The API uses standard HTTP status codes. Most error responses are a plain-text message. When the body of a request that creates or updates a consignor or payout fails validation, the response is JSON instead, with an errors object that lists the messages for each field:
{ "errors": { "email": [ "Please enter a valid email address" ] } }