Code Logo

This page contains the technical documentation of the Code R&E API. The API can be used to push data towards the R&E application in case of certain events, as well as to receive updates from the RMA requests (return merchandise authorization) done through the application by webhooks.

The API is generally interesting for 3rd parties integrating with the Code R&E application, e.g. third party logistics, shipping parties, and parties that gather statistics.

How to connect?

Currently the API is opened up to merchants and integrators on a per-request basis. Please contact us here to find out more!

Security

There are two options for security measures in place to ensure the integrity of the data flowing through the API:

  • HMAC: The message will be accompanied by a HMAC value that can be used to verify the integrity and origin of the message.
  • JWT: The message will be accompanied by a JWT token that can be used to verify the authenticity of the sender.

HMAC

HMAC is used to check the integrity and origin of the incoming event message. In short, it guarantees that the message coming in is written by someone who owns the same secret as was entered into the R&E application, without exposing this secret in the message itself. Note; it does not encrypt the message itself, this is left to the https protocol.

The HMAC value should be calculated by creating a digest in hexadecimal format using of the request body using the configured hash algorithm (default sha1) and secret. Digests can easily be created using cryptographic packages in your language/environment of choice (NodeJS, Python, Java, C++, Rust) or on the commandline:

echo -n "<data>" | openssl dgst -sha1 -hmac "<secret>"
> 63ac6b48f0c7ce19ad954aa1acfd093611066abe

JWT

JWT is used to check the integrity and origin of the incoming event message. In short, it guarantees that the message coming in is written by someone who owns the same secret as was entered into the R&E application, without exposing this secret in the message itself. Note; it does not encrypt the message itself, this is left to the https protocol.

To create a JWT token, you need to post the following data to the JWT provider, which is Google's Identity Toolkit:

{
  "email": "<Account email>",
  "password": "<Account password>",
  "returnSecureToken": true
}

The endpoint to POST to for a token is https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=<APIKey> where APIKey is AIzaSyBnBpeMxZCQ1VObDVmyAt2rqlWCeH8YuxI for regular Shopify app installs. Contact us for the API key for your install if you didn't install it through the Shopify App Store.

Flows

The API consists of three flows, in, out, and search:

  • The in flow is served by an endpoint /event that received POST calls for relevant events at the 3rd-party.
  • The out flow is served by webhooks. On specific events within the application a webhook handling endpoint can get called with data related to the RMA request.
  • The search flow is served by an endpoint /search that can be used to search for RMA requests and order information in the application.

In flow

Currently there is only one flow into the application from external parties on the API. This is through the /event endpoint. This endpoint is fully documented in the Swagger interface and the OpenAPI specification.

The endpoint is secured trough https and expects both a rma-shop-domain and rma-hmac-sha header or shop and hmac parameter in the url. Alternatively, you can use a JWT token instead of the HMAC by using it as the Authorization header. So all of the following request would be accepted:

curl \
    --request POST '<endpoint>/event' \
    --header 'rma-hmac-sha: <hmac>' \
    --header 'rma-shop-domain: <yourshop>.myshopify.com' \
    ...
curl \
    --request POST '<endpoint>/event?hmac=<hmac>&shop=<yourshop>.myshopify.com' \
    ...
curl \
    --request POST '<endpoint>/event?shop=<yourshop>.myshopify.com' \
    --header 'Authorization: Bearer <JWT>' \
    ...
curl \
    --request POST '<endpoint>/event' \
    --header 'Authorization: Bearer <JWT>' \
    --header 'rma-shop-domain: <yourshop>.myshopify.com' \
    ...

Shop

Included in the header or url query we also expect the name of the shop for which this event is fired. This value identifies the shop this event is for, and is coupled to your HMAC secret. An example of a valid shop name is:

my-favorite-shop.myshopify.com

Note that this is the exact domain name Shopify reserved for your shop, ignoring any custom domains.

Data

The data in the request body to be sent over the endpoint differs per event. It should always be valid JSON. At the moment only the received event is supported.

Received

The received event schema consists of a type "received", a timestamp (ISO), the RMA reference (name), an optional problem string, and a list of items that were received:

{
  "type": string,
  "timestamp": string,
  "rmaReference": string,
  "problem": string | undefined,
  "rmaItems": [
    {
      "sku": string,
      "quantity": number
    }
  ]
}

The RMA request will usually flow into the Returned state in the app after receiving the received event including all items. Note that currently there is no support for splitting the received items across multiple received events. The event is considered the only and final received event on the referenced RMA. Unless the response is not 200, any subsequent calls will be rejected.

Example

Example request to notify the application of receiving back a damaged item with SKU ABC (using secret as the secret key).

curl \
    --request POST '<endpoint>/event' \
    --header 'rma-hmac-sha: ff90710be02846277954fee67992af44864f6cea' \
    --header 'rma-shop-domain: allvital-demo.myshopify.com' \
    --header 'Content-Type: application/json' \
    --data-raw '{"type":"received","timestamp":"2022-05-18T11:07:21.223Z","rmaReference":"CODE-123-R1","problem":"Damaged during transport","rmaItems":[{"sku":"ABC","quantity":1}]}'

Out flow

The out flow serves the purpose of notifying external parties of updates in de application. When certain state changes happen, the application fires a POST request towards a predefined URL (webhook) with the RMA request state in JSON format. The request body contains the current state of the RMA request as well as the type of event that was fired.

The request is also signed using HMAC in a similar way as for the in flow, using an algorithm (default sha1) and a shared secret. It is up to the webhook receiving endpoint to check the HMAC, and optionally check for replay attacks using the timestamp in the request body.

The headers contain the following application specific keys:

  • rma-shop-domain: the shop for which this webhook was fired,
  • rma-hmac-sha: the HMAC calculated from the request body and the secret, using the specified algorithm,
  • pid: a process ID for the webhook executor,

The full schema of the request body can be found in Swagger under the EventOutput schema. An example of such a request body can be found below.

{
  "timestamp": "2022-05-21T14:25:42.231Z",
  // Name (reference) of the RMA request.
  "rmaReference": "CODE-123-R1",
  "type": "processed",
  // Varies for each state change, check Swagger for the options.
  "customerNote": "Such beautiful pants! Unfortunately, I ordered the wrong color.",
  "customer": "Jan Janssen",
  "originalOrder": {
    "id": 8351047361539,
    // Shopify Order ID.
    "name": "CODE-123",
    "shippingCountry": "Netherlands",
    "shippingCountryCode": "NL"
  },
  "exchangeOrder": {
    "id": 8351047361538,
    // Shopify Order ID.
    "name": "CODE-122",
    "shippingCountry": "Netherlands",
    "shippingCountryCode": "NL"
  },
  // Some states can be skipped, so some parts in the timeline might not
  // be set.
  "timeline": {
    "orderCreatedAt": "2022-05-15T09:00:47.043Z",
    "rmaSubmittedAt": "2022-05-19T09:01:26.674Z",
    "rmaShippedAt": "2022-05-19T17:41:12.912Z",
    "rmaPaymentPendingAt": null,
    "rmaReturnedAt": "2022-05-20T10:05:05.772Z",
    "rmaProcessedAt": "2022-05-21T14:25:40.113Z"
  },
  // Some states can be skipped, so some parts in the status might remain
  // false even after processing.
  "status": {
    "isSubmitted": true,
    "isUnprocessable": false,
    "isShipping": true,
    "isReturned": true,
    "isPaymentPending": false,
    "isProcessed": true,
    "hasError": false,
    "hasFlag": false
  },
  "rmaItems": [
    {
      "returned": true,
      "type": "return",
      // Can be "return" or "exchange".
      "returnProduct": {
        "lineItemId": 54987954987332,
        // Order Line Item ID
        "productId": 73849201735194,
        // Shopify product ID.
        "productTitle": "Clown Pants",
        "variantId": 82923409292394,
        // Shopify variant ID.
        "variantTitle": "23 / 64",
        "sku": "ABC"
      },
      "reasons": [
        {
          "id": "2",
          // Return reason ID as defined in the R&E application.
          "label": "Color not as expected"
          // Human readable return reason.
        }
      ]
    }
  ]
}

Search flow

The search flow is a utility that external parties can use to search for RMA requests in the application. It is a simple GET request that returns a list of RMA requests that match the search query.

The query parameters that can be used to search are:

  • rma: the RMA reference (name) of the RMA request (e.g CODE-123-R1),
  • order: the Shopify order name of the original order (e.g. CODE-123),
  • trackingCode: the tracking code of the RMA request (e.g. 3SABCD123456789),

An example of a search request:

curl -X 'GET' \
  '<endpoint>/search?trackingCode=3SYZXG7532624' \
  --header 'Authorization: Bearer <token>'

The response is a list of RMA requests and orders that match the query parameters, e.g.:

[
  {
    "shop": "allvital-demo.myshopify.com",
    "order": {
      "name": "CODE-DEV1596",
      "customer": {
        "name": "Yuri van Geffen"
      },
      "shippingAddress": {
        "countryCode": "NL"
      },
      "items": [
        {
          "name": "Baristas * The Longs - 28 / 32",
          "sku": "8720195081241",
          "barcode": "8720195081241",
          "quantity": 1,
          "thumbnail": "https://code.nl"
        }
      ]
    },
    "rma": {
      "name": "CODE-DEV1596-R1",
      "note": "",
      "items": [
        {
          "sku": "8720195081241",
          "quantity": 1
        }
      ]
    }
  }
]