23andMe Fulfillment API

Fulfill orders through an easy, REST-ful API.


Authentication

The API performs basic authentication (username, password) to authenticate API users before they can use API. Please coordinate with the 23andMe Fulfillment team to obtain API credentials.

Usage Overview

The following is a list of the main usages of the API.

  • Fulfill Shipments

  • The API allows partners to retrieve available orders, fulfill them, and mark them as shipped. The API user primarily will interact with the shipment endpoint (api/v1/shipment/) to update information regarding the progress of fulfilling an order, including when adding tracking numbers, carrier information, part numbers, etc.

  • Fulfill cartons of samples

  • Fulfillment partners are able to use the API to ship cartons of samples from distribution centers to a lab destination. Partners can create and update a carton of samples. An example of information to update a carton with includes shipping tracking information or a list of vendor codes associated with the samples. Refer to the entry on the carton (/api/v1/carton) endpoint for additional details.

  • Communicate Updates To Samples

  • Updates to a sample throughout its lifecycle can be sent communicated through the API. Once a sample arrives at a distribution center, partners can communicate this by using the sample (/api/v1/sample) endpoint. Partners can use the return (/api/v1/return) endpoint to mark samples as having returned to 23andMe's inventory. When samples are disposed of, fulfillment partner can invoke the scrap_kit (/api/v1/scrap_kit) endpoint to mark them as scrapped.

Endpoints

Endpoint
Description and example call and response
GET /api/v1/shipment/?format=json

Lists all shipments that can be fulfilled or are being fulfilled by the warehouse. Send shipment_status as an optional URL filter parameter ('N' for all new shipments, 'R' for shipments currently in fulfillment but not yet shipped). No provided shipment_status will return all shipments with either status.

View curl example

# Call
curl https://fulfillment.23andme.io/api/v1/shipment/?shipment_status=N&format=json

# Response
{
    "meta": {
        "limit": 20,
        "next": "/api/v1/shipment/?offset=20&limit=20&format=json",
        "offset": 0,
        "previous": null,
        "total_count": 22
    },
    "objects": [
        {
            "id": "744631937068271-2",
            "resource_uri": "/api/v1/shipment/744631937068271-2/",
        },
        ...
    ]
}
            
GET /api/v1/shipment/XXX-Y/?format=json

Lists an individual shipment's data.

View curl example

# Call
curl https://fulfillment.23andme.io/api/v1/shipment/716248990674159-1/?format=json

# Response
{
    "comments": "",
    "customer_id": "983",
    "freight_term": "Third Party",
    "id": "716248990674159-1",
    "number_line_items": 1,
    "order_items": [
        {
            "commodity_code": "3926.90.9910",
            "country_of_origin": "CA",
            "description": "Personal Genome Service",
            "discounted_price": "99.00",
            "extended_price": "99.00",
            "id": 765828782327386,
            "kit_label_description": "",
            "part_number": "OGI-500",
            "quantity": 1,
            "unit_price": "15.00",
            "unit_weight": 150,
            "vendor_code": 12345678901234,
        }
    ],
    "order_status": "F",
    "order_status_label": "Fulfillment",
    "resource_uri": "/api/v1/shipment/716248990674159-1/",
    "ship_account_number": "846547419",
    "ship_address_1": "PO Box 333",
    "ship_address_2": "",
    "ship_address_3": "",
    "ship_city": "San Juan",
    "ship_company": "",
    "ship_country": "PR",
    "ship_first_name": "Anita",
    "ship_last_name": "Mann",
    "ship_method": "dhl",
    "ship_phone": "1111111111",
    "ship_state": "gg",
    "ship_zip": "00026",
    "shipment_status": "N",
    "total_order_value": "15.00"
}
            
PATCH /api/v1/shipment/XXX-Y/?format=json

Marks an order for fulfillment or update the tracking number. Send shipment_status as a URL parameter to mark the order for fulfillment. Send tracking_number to update the tracking number for this order. You don't have to send both parameters.

View curl example

# Call for shipment_status
curl "https://fulfillment.23andme.io/api/v1/shipment/716248990674159-1/ \
        ?format=json&shipment_status=R"
    -X PATCH

# Call for tracking_number
curl "https://fulfillment.23andme.io/api/v1/shipment/716248990674159-1/ \
        ?format=json"
    -X PATCH -d '{"tracking_number":012345678901234}'

# Response
(Returns a 204 NO CONTENT on success; use the GET to verify changed data.)
            
POST /api/v1/shipment/XXX-Y/?format=json

Add a shipping receipt to a shipment item. Send shipping_method, time_shipped, tracking_number, items, and optional bulk_tracking_number. items is a list of dicts; each element contains id, return_tracking_number, vendor_code, part_number, and optional carrier.

Three API formats are supported. In the first, each of the 5 top-level parameters is supplied separately JSON encoded. In the second format, a single JSON encoded dictionary is supplied. The third format is a bulk style API which use bulk_order_line id.

View curl example

# Call using older API
curl https://fulfillment.23andme.io/api/v1/shipment/716248990674159-1/?format=json \
    -d "shipping_method=USPS" \
    -d "time_shipped=2014-04-01 12:12:12" \
    -d "tracking_number=1234567890123456789012345678901234" \
    -d "items=[{\"id\": 1237123, \"return_tracking_number\": 4891729871, \"vendor_code\": 12345678901234, \"part_number\": \"OGI-500\", \"carrier\": \"DHL\"}]"

# Or call using newer (9/2014)  API
curl https://fulfillment.23andme.io/api/v1/shipment/716248990674159-1/?format=json \
    -d '{"shipping_method:USPS","time_shipped":2014-04-01 12:12:12' \
    ',"tracking_number": "1234567890123456789012345678901234"' \
    ',"items":[{"id": 1237123, "return_tracking_number": "4891729871"' \
    ', "vendor_code": "12345678901234", "part_number": "OGI-500", "carrier": "DHL"}]}'

# Or call using bulk style (3/2016)  API
curl https://fulfillment.23andme.io/api/v1/shipment/716248990674159-1/?format=json \
    -d '{"shipping_method":"USPS","time_shipped":"2017-11-07 12:12:12",'\
    '"tracking_number": "1234567890123456789012345678901234","items":[{"bulk_line_id": 687834569072218,'\
    '"part_number": "HUXX-00-N05","carrier":"DHL"}, [{"return_tracking_number":'\
    '"4891729871", "vendor_code": "12345678901234"}]]}'

# Response
(Returns a 204 NO CONTENT on success; use the GET to verify changed data.)

# Or call with the "post_async" parameter (Dec 2019) to asynchronously POST a shipment.
# Expect a HTTP 202 response with an estimate of how long the shipment will take to complete.
# Wait until the estimate elapses before repeatedly making the same POST request (with the "post_async" parameter)
# to receive updates about the result of the shipment request until you receive a HTTP 204
# or an error code (HTTP 40X or 50X).
curl https://fulfillment.23andme.io/api/v1/shipment/716248990674159-1/?format=json \
    -d '{"shipping_method:USPS","time_shipped":2014-04-01 12:12:12'\
    ',"tracking_number": 1234567890123456789012345678901234'\
    ',"post_async": 1'\
    ',"items":[{"id": 1237123, "return_tracking_number": "4891729871"'\
    ',"vendor_code": "12345678901234", "part_number": "OGI-500", "carrier": "DHL"}]}'

# Response
(Returns a 202 ACCEPTED when first receiving the request with the "post_async" parameter.)
(Returns a 204 NO CONTENT on success upon successive shipment POST requests. Use GET method to verify changed data.)
            
GET /api/v1/carton/?format=json

Lists all cartons associated with this distribution center (default for this user). Send status as a URL parameter (‘S’ to get all shipped cartons, ‘N’ for all new, unshipped cartons).

View curl example

# Call
curl "https://fulfillment.23andme.io/api/v1/carton/?format=json&status=N"

# Response
{
    "meta": {
        "limit": 20,
        "next": null,
        "offset": 0,
        "previous": null,
        "total_count": 2
    },
    "objects": [
        {
            "id": null,
            "resource_uri": "/api/v1/carton/1/",
            "status": "N",
            "external_carton_id": "ABC23",
        },
        ...
    ]
}
                
GET /api/v1/carton/XXX/?format=json

Lists an individual carton’s data.

View curl example

# Call
curl https://fulfillment.23andme.io/api/v1/carton/2/?format=json

# Response
{
    "carrier": "DHL",
    "date_shipped": "2014-05-09T11:38:38",
    "external_carton_id": "ABC23",
    "id": "2",
    "lab_id": 2,
    "resource_uri": "/api/v1/carton/2/",
    "samples": [
        "84769754409636",
        "53673257725370",
        "72175981970240",
        "96641311095096",
        "78784546468567"
    ],
    "status": "S",
    "tracking_number": "456789456123"
}
                
POST /api/v1/carton/?format=json

Creates a new carton. Optional external_carton_id parameter can be sent with the POST. If not provided, defaults to null. Optional from parameter can be sent to indicate carton is from the non-default location for this API user, e.g. "bedford", "oss", or "think".

View curl example

# Call
curl -X POST https://fulfillment.23andme.io/api/v1/carton/?format=json \
       -d "external_carton_id=ABC23" \

# Response
(Returns a 201 CREATED on success with the object in the body.
The object url is also listed in the Location header)

{
    "carrier": null,
    "date_shipped": null,
    "external_carton_id": "ABC23",
    "id": "3",
    "lab_id": 2,
    "resource_uri": "/api/v1/carton/3/",
    "samples": [],
    "status": "N",
    "tracking_number": null
}
                
POST /api/v1/carton/XXX/?format=json

Updates all attributes, adds samples, and marks the carton as shipped. Send lab_id, carrier, status ('S' for Shipped), tracking_number, date_shipped, and samples which is a JSON-encoded list of barcodes.

Tracking number must be present but can be an empty string to be patched in later.

View curl example

# Call
curl https://fulfillment.23andme.io/api/v1/carton/3/?format=json \
       -d "lab_id=2" \
       -d "carrier=DHL" \
       -d "status=S" \
       -d "tracking_number=456789456124" \
       -d "date_shipped=2014-05-09 11:38:38" \
       -d "samples=[78923498743267, 78923498743268, 78923498743269, ...]"

# Response
(Returns a 204 NO CONTENT on success; use the GET to verify changed data.)
                
PATCH /api/v1/carton/XXX/?format=json

Updates some attributes on a previously shipped carton. Can be any of passed carrier, tracking_number, date_shipped, external_carton_id.

View curl example

# Call
curl https://fulfillment.23andme.io/api/v1/carton/3/?format=json -X PATCH \
       -d "carrier=DHL" \
       -d "tracking_number=456789456124"

# Response
(Returns a 204 NO CONTENT on success; use the GET to verify changed data.)
                
PATCH /api/v1/sample/YYY/?format=json

Mark a sample barcode as arrived at the Distribution Center. 'YYY' is the sample barcode. Send arrival_time as a parameter.

View curl example
# Call
curl "https://fulfillment.23andme.io/api/v1/sample/84769754409636/ \
         ?format=json" -X PATCH \
     -d "arrival_time=2014-05-12 11:38:38"

# Response
(Returns a 204 NO CONTENT on success)

                
PATCH /api/v1/return/YYY/?format=json

Return a kit to inventory. 'YYY' is the sample barcode.

View curl example
# Call
curl "https://fulfillment.23andme.io/api/v1/return/84769754409636/ \
         ?format=json" -X PATCH \

# Response
(Returns a 200 OK on success)

                
PATCH /api/v1/scrap_kit/YYY/?format=json

Process a scrap end user return. 'YYY' is the sample barcode. An optional 'note' can be included in the request data to override the default "Scrap End User Return Invalidation", but the default is preferred.

View curl example
# Call
curl "https://fulfillment.23andme.io/api/v1/scrap_kit/84769754409636/ \
         ?format=json" -X PATCH \
         -d "note=Scrap End User Return Invalidation" \

# Response(s)
(Returns a 200 OK on success, indicating the kit can be discarded)
(Returns a 422 UNPROCESSABLE ENTITY on failure, with additional message indicating a review
party has been contacted and to hold the kit and any related material until said party can
review the case)

                
GET /api/v1/bulk/YYY?format=json

Requests information on previous ACH-mediated bulk retail order: 'YYY' is either the retail_order_id or the shipment_id returned from the POST which created the request.

View curl example
# Call
curl "https://fulfillment.23andme.io/api/v1/bulk/716248990674159-1?format=json"

# Response
{
    "ach_order_id": "12345678",
    "other_field": "2014-05-09T11:38:38",
    ...
}
POST /api/v1/bulk/?format=json

Creates a new ACH-mediated bulk retail order. Required parameters include ach_order_id, lines. The request returns both the retail_order_id and the shiment_id. The shipment_id will generally be in status NEW and will not show as eligible for fulfillment for some time.

View curl example
# Call
curl https://fulfillment.23andme.io/api/v1/bulk/?format=json \
    -d '{"ach_order_id" :"12345678"","VendorID" :"myFavoriteVendor" \
    ', "PONumber": 1234567890123456789012345678901234', '"WarehouseID": 5'\
    ',"lines":[{"quantity": 456, "part_number": "RUXX-00-N05"'\
    ', "RetailSKU": 12345678901234, "UPC": "165100004324" },\
    {"quantity": 444, "part_number": "RUXX-00-N05"'\
            ', "RetailSKU": 12345678901234, "UPC": "165100004324" }
     ]}'

# Response
{
    "ach_order_id": "12345678",
    "retail_order_id": "813228966672189",
    "shipment_id": "716248990674159-1",
}
GET /api/v1/barcode_status?barcodes=X,Y,Z,...&format=json

Retrieves barcodes grouped by their status and a count of the number of barcodes in each status. The API expects the barcodes be provided in one of two ways. The barcodes can be a query parameter string separated by commas or as a JSON array. To provide barcodes in the JSON request body, it would have to adhere to the following pattern:

View request body example
                # Call
                curl https://fulfillment.23andme.io/api/v1/barcode_status?format=json
                { "barcodes": [1,2,3,4,5,6] }
            

If barcodes are not provided or barcodes that don't correspond to a sample are provided, the API will return a HTTP 400 Bad Request response. Below is an example of a call to the barcode_status API with barcodes provided in the query parameters.

View call example
                # Call
                curl https://fulfillment.23andme.io/api/api/v1/barcode_status?barcodes=123,124,125&format=json

                # Response
                { "objects": [{
                    "Invalid": {
                        "barcodes": ["124"],
                        "can_return_to_inventory": false,
                        "count": 1, },
                    "Valid": {
                        "barcodes": ["123", "125"],
                        "can_return_to_inventory": true,
                        "count": 2,
                    }}]}
            
POST /api/v1/retail_order/

Creates a new sales order with the provided data parameters. Returns a JSON payload of the order, if created properly, and its attributes. This is a way of creating bulk orders that are sold by business parties (e.g. Amazon US).

View call example
            # Call
            curl -X POST https://fulfillment.23andme.io/api/v1/retail_order/ \
                -d '{' \
                        '"ns_internal_id": "1234124", "business_party": "sales_us_amazon",' \
                        '"category": "expense_sales",' \
                        '"lines": [{"quantity": 10, "part_number": "HUXX-00-N08", "product_id": 64}]' \
                        ...
                    }'

            # Response
            {
                "ns_internal_id": "1234124",
                "business_party": "sales_us_amazon",
                "category": "expense_sales",
                ...
                "time_created": "2021-01-01T12:00:00"
            }
            
GET /api/v1/retail_order/

Returns a JSON payload that describes a sales order that matches the provided query parameters.

View call example
            # Call
            curl -X GET https://fulfillment.23andme.io/api/v1/retail_order/ \
                ?ns_internal_id=XYZ

            # Response
            {
                "ns_internal_id": "XYZ",
                "business_party": "sales_us_amazon"
                "category": "expense_sales"
                "order_status": "FULFILLMENT",
                "shipment_status": "IN_FULFILLMENT",
                ...
                "time_created": "2021-01-01T12:00:00",
            }
            

Authentication

We use HTTP basic authentication. Send an Authorization: Basic b64encode(username:password) along with requests to the endpoints. Contact us for a username and password.


Fulfillment Process Steps

Note: We have both orders and shipments. A single order can have multiple shipments (for reshipments only; we do not currently split kits from an order into separate shipments). A shipment will only be associated with one order. The status of both the Order and the Shipment matter for many fulfillment steps. These will be pointed out when pertinent. A list of possible statuses can be found at the end of this document.


  1. Get all new shipments ready for shipping that are assigned to your warehouse:

    GET /api/v1/shipment/?shipment_status=N&format=json

    Adding the shipment_status=N parameter ensures that only orders that have not yet been claimed for fulfillment will show up.

    Note that you can also see all shipments that have been claimed for fulfillment by your warehouse by changing the shipment_status parameter to ‘R’. ‘N’ and ‘R’ are the only allowable shipment_status filters. Not including a shipment_status parameter will return all shipments in status ‘R’ OR ‘N’ for your warehouse.

  2. You can see detailed data for a particular shipment by sending a GET request with the ID (in format XXX-Y):

    GET /api/v1/shipment/XXX-Y/?format=json

    This will return information about the shipment. Included in this information is the order_status and shipment_status for this shipment.

  3. When you are ready to import a shipment into your system, send a PATCH request to that particular shipment endpoint. This should be done before any actual work is done to ship the shipment. Until this step is performed, a customer can still cancel their order.

    PATCH /api/v1/shipment/XXX-Y/?format=json&shipment_status=R

    Include the shipment_status=R parameter.

    For the PATCH to succeed, the starting shipment_status must be ‘N’ and the order_status must be ‘A’ or ‘C’. After a successful request, the shipment_status will become ‘R’ and the order_status will become ‘F’ (if it was ‘A’) or stay ‘C’ (if it was ‘C’).

  4. When a shipment is packaged and ready to ship, send a POST request to the particular shipment endpoint with all necessary parameters.

    POST /api/v1/shipment/XXX-Y/?format=json

    See the documentation for this endpoint for the necessary parameters. Note that the item id’s come from the shipment detail GET (step 2) -- they are within the order_items list.

    The starting shipment_status must be ‘R’ for this POST to succeed. After a successful POST, the shipment_status will be ‘S’. Note that ALL order items (kits) associated with this shipment must be sent in a single POST request or the POST will fail.


Distribution Center Steps

After the customer has spit into his/her kit and returned it to the distribution center, the steps below should be followed:

  1. When a sample (kit) is received at the distribution center, a PATCH request should be sent to the sample endpoint:

    PATCH /api/v1/sample/YYY/?format=json

    YYY is the sample barcode (not the tracking number). The arrival time should be included as a parameter.

    The sample must have starting status ‘V’ for the request to be successful. You will not have control over or visibility into the sample status, but will receive an error message back if the starting status is incorrect. You should contact 23andMe if this happens so we can trace what went wrong.

  2. Create a new, empty carton by sending a POST request:

    POST /api/v1/carton/?format=json

    An optional external_carton_id parameter can be provided if you have an internal ID number for this carton. 23andMe will create its own carton number and return this back to you in the body of the response and also in the Location header.

  3. To view details for the newly created carton:

    GET /api/v1/carton/XXX/?format=json

    XXX comes from the 23andMe carton id returned to you from the POST in step 2.


    Also relevant: if you wish to view all cartons that are associated with your distribution center:

    GET /api/v1/carton/?format=json

    Add an optional ‘status’ parameter if desired: ‘S’ for all shipped cartons, ‘N’ for all new, unshipped cartons.

  4. To ship a carton, send a POST request to the specific carton endpoint with all necessary parameters, including the sample barcodes in the carton.

    POST /api/v1/carton/XXX/?format=json

    XXX is the carton id returned from the creation POST in step 2. The required attributes can be found in the documentation for this endpoint.

    Samples being added to the carton must have a status of ‘C’ (which they should have if the PATCH from step 1 was successful). The carton must be in status ‘N’. All samples that are being shipped in a carton must be added at the same time in one POST request (this request both adds the samples to the carton and marks the carton as shipped). A successful POST will change the carton status to ‘S’.


Status Possibilities

Order Statuses:

STATUS_NEW = 'N'
STATUS_HELD = 'H'
STATUS_APPROVED = 'A'
STATUS_CANCELLED = 'X'
STATUS_IN_FULFILLMENT = 'F'
STATUS_COMPLETED = 'C'
STATUS_AUTO_FULFILLED = 'U'

Shipment Statuses:

STATUS_NEW = 'N'
STATUS_IN_FULFILLMENT = 'R'
STATUS_CANCELLED = 'X'
STATUS_FULFILLED = 'F'
STATUS_SHIPPED = 'S'

Carton Statuses:

STATUS_NEW = 'N'
STATUS_SHIPPED = 'S'
STATUS_RECEIVED = 'R'
STATUS_CANCELLED = 'X'