Connect to Carrier Services

fulfillmenttools offers a convenient way to send and track parcels. This page tells you more about the utilization of this feature.

fulfillmenttools can connect to various carriers allowing you to create shipments and corresponding parcels. Please clarify beforehand which carriers should be made available to your instance.

However, the usage of existing & ready to use carriers is similar and can be used no matter how many carriers you operate.

Entities of importance in this context

Carrier

Courier, Express and Parcel Services (or in Short: CEP) refer to entities, that are in general configured in fulfillmenttools. It depends on your setup which CEP Providers are available & ready to use. The API description including some documentation about the used attributes can be found in our API reference

To get a list of available carriers you can issue the following call:

curl -sSL 'https://your.api.fulfillmenttools.com/api/carriers' \
  --header 'Authorization: Bearer <TOKEN>'

200 OK
{
    "total": 5,
    "carriers": [
        {
            "id": "5s6fF85BaTnQ9f5uT6uaSC",
            "name": "GLS",
            "key": "GLS",
            "deliveryType": "DELIVERY",
            "status": "INACTIVE",
            "version": 2
        },
        {
            "id": "9GvnfJQxdzBcQThaawZjgm",
            "name": "ANGEL",
            "key": "ANGEL",
            "deliveryType": "SAMEDAY",
            "status": "ACTIVE",
            "version": 1
        },
        {
            "id": "b20fbff2-3443-4969-9b9b-f59dff9e875e",
            "name": "DHL_V2",
            "key": "DHL_V2",
            "deliveryType": "DELIVERY",
            "status": "ACTIVE",
            "version": 2
        },
        {
            "id": "b1d65915-6fac-43a5-ac73-695b9f99dfef",
            "name": "Local Delivery Partner in Cologne",
            "key": "CUSTOM",
            "deliveryType": "DELIVERY",
            "status": "ACTIVE",
            "version": 1
        },
        {
            "id": "703f43cb-b15e-44ca-bbb4-56e403e59740",
            "name": "FEDEX",
            "key": "FEDEX",
            "deliveryType": "DELIVERY",
            "status": "ACTIVE",
            "version": 3
        }
    ]
}

The response gives you a list of configured carriers, along with some general information and their key, which describes which kind of implementation is chosen by fulfillmenttools whenever this carrier is used to, for example, issue a label request or setup track and trace connection for a parcel. You can get a more detailed look at a configured carrier by requesting one identified carrier instance, for example:

curl -sSL 'https://your.api.fulfillmenttools.com/api/carriers/b20fbff2-3443-4969-9b9b-f59dff9e875e' \
  --header 'Authorization: Bearer <TOKEN>'

200 OK
{
    "key": "DHL_V2",
    "name": "DHL_V2",
    "status": "ACTIVE",
    "defaultParcelWeightInGram": 1000,
    "parcelLabelClassifications": [
        {
            "nameLocalized": {
                "en_US": "Parcel 2 kg",
                "de_DE": "Paket 2 kg"
            },
            "dimensions": {
                "weight": 2000,
                "length": 60,
                "height": 15,
                "width": 30
            },
            "name": "Paket 2 kg"
        },
        {
            "nameLocalized": {
                "en_US": "Parcel 5 kg",
                "de_DE": "Paket 5 kg"
            },
            "dimensions": {
                "weight": 5000,
                "length": 120,
                "height": 60,
                "width": 60
            },
            "name": "Paket 5 kg"
        },
        {
            "nameLocalized": {
                "en_US": "Parcel 10 kg",
                "de_DE": "Paket 10 kg"
            },
            "dimensions": {
                "weight": 10000,
                "length": 120,
                "height": 60,
                "width": 60
            },
            "name": "Paket 10 kg"
        },
        {
            "nameLocalized": {
                "en_US": "Parcel 31,5 kg",
                "de_DE": "Paket 31,5 kg"
            },
            "dimensions": {
                "weight": 31500,
                "length": 120,
                "height": 60,
                "width": 60
            },
            "name": "Paket 31,5 kg"
        }
    ],
    "lifecycle": "GA",
    "deliveryType": "DELIVERY",
    "id": "b20fbff2-3443-4969-9b9b-f59dff9e875e",
    "version": 2,
    "created": "2023-03-07T10:53:32.237Z",
    "lastModified": "2023-03-20T10:27:46.231Z"
}

A Carrier instance is a singleton in any given fulfillmenttools platform. That means in general it can exist exactly once per tenant.

As you can see in the above response there are some configurations made to this exact carrier (DHL in this case). These configurations can be done on every carrier instance (check Add Carrier for details or continue reading.

Creation of a carrier

A carrier can be created by issuing the following call:

curl -sSL -X POST 'https://your.api.fulfillmenttools.com/api/carriers' \
--header 'Authorization: Bearer <TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
                "key": "DHL_V2",
                "name": "DHL",
                "status": "ACTIVE",
                "defaultParcelWeightInGram": 1000,
                "credentials": {
                        "key": "DHL_V2",
                        "apiKey": "myApiKey",
                        "fallback": {
                            "key": "DHL_V2",
                            "billingNumber": "XYZ",
                            "retoureBillingNumber": "optionalXYZ",
                            "dhlBusinessUsername": "Username from DHL Business customer portal (Geschäftskundenportal)",
                            "dhlBusinessPassword": "Password from DHL Business customer portal (Geschäftskundenportal)"
                        }
                    },
                "parcelLabelClassifications": [
                    {
                        "nameLocalized": {
                            "en_US": "Parcel 2 kg",
                            "de_DE": "Paket 2 kg"
                        },
                        "dimensions": {
                            "weight": 2000,
                            "length": 60,
                            "height": 15,
                            "width": 30
                        },
                        "name": "Paket 2 kg"
                    },
                    {
                        "nameLocalized": {
                            "en_US": "Parcel 5 kg",
                            "de_DE": "Paket 5 kg"
                        },
                        "dimensions": {
                            "weight": 5000,
                            "length": 120,
                            "height": 60,
                            "width": 60
                        },
                        "name": "Paket 5 kg"
                    },
                    {
                        "nameLocalized": {
                            "en_US": "Parcel 10 kg",
                            "de_DE": "Paket 10 kg"
                        },
                        "dimensions": {
                            "weight": 10000,
                            "length": 120,
                            "height": 60,
                            "width": 60
                        },
                        "name": "Paket 10 kg"
                    },
                    {
                        "nameLocalized": {
                            "en_US": "Parcel 31,5 kg",
                            "de_DE": "Paket 31,5 kg"
                        },
                        "dimensions": {
                            "weight": 31500,
                            "length": 120,
                            "height": 60,
                            "width": 60
                        },
                        "name": "Paket 31,5 kg"
                    }
                ]
            }'

As you can see there are already some configurations to this carrier provided upon creation. Some of the interesting data is

AttributeDescription

defaultParcelWeightInGram

This value depicts, no surprise here, the default value (1000 gram) to a parcel advised using this carrier. However, the actual weight might be overwritten by the actual call for the creation of a parcel (see Add Parcel in our API Specification).

parcelLabelClassifications

This array of values describes which parcel dimensions should be selectable by a client when issuing a parcel. Please see, that these values are dependent on the carrier and your contract with it. It also maybe not be a mandatory value and could possibly be omitted.

credentials

The credentials that are used to connect to the carriers' API. Which kind of credentials is needed depends on the carrier (selected by the attribute key. In this case the DHL API in Version 2 is selected. Thus we need to provide an API Key. Also there is (especially for DHL) the possibility to provide Fallback Credentials using the legacy DHL API ("DHL Geschäftskunden API" in this case.)

logoUrl

This optional attribute can be used to set a specific logo to a custom carrier or to overwrite the fallback logos for standard carriers provided by fulfillmenttools if needed.

Carrier specific configuration

Depending on the used Carrier implementation (depicted by the tag key) some additional configuration can be used to successfully operate a carrier:

curl -sSL 'https://your.api.fulfillmenttools.com/api/carriers/488bd1a6-6c76-4407-8dab-ece716dac14d/configuration' \
  --header 'Authorization: Bearer <TOKEN>'

200 OK
{
    "id": "DHL_V2",
    "version": 9,
    "carrierRef": "488bd1a6-6c76-4407-8dab-ece716dac14d",
    "created": "2022-10-19T10:02:03.545Z",
    "lastModified": "2023-04-03T11:07:21.169Z",
    "returnLabel": false,
    "mustBeWeighed": false
} 

One of the most important configurations is the attribute returnLabel in a given configuration. It tells fulfillmenttools whether a return label should be created with every label requested from the given carrier.

Please refer to Get Carrier Configuration from API and Change Configuration via API in our Swagger Documentation to understand which kind of configuration is necessary / advised.

Facility

This resource describes the stores, warehouses, popup stores, locations, etc. of the fulfillment network. In general, a facility is a place where fulfillment processes take place or should be considered. Details see use case Add and Manage Facilities

In this carrier services related use case a facility is a place where CEP services pick up parcels or inter facility transfers are started / finished.

Connection between a Facility and a Carrier

The Facility-To-Carrier-Connection is (some people already understood) the connection between a facility and a carrier. Many configurations that allow for frictionless carrier services have to be configured here. Please refer to FacilityCarrierConnection for detailed information on every attribute. Here are some of the attributes worth mentioning:

AttributeDescription

cutoffTime

Value (hour and minute) describe the time when the carrier (usually) shows up to take parcels with him. This information is especially useful when configuring SAMEDAY deliveries as it is crucial to have finished any fulfillment process until the arrival of the carrier.

deliveryAreas

This optional value might be of special interest to Custom Carriers and specific SAMEDAY carriers. It shows which target addresses (identified by an array of ISO 3166-1 alpha-2 conform country codes and their respective postalcodes) can be a valid target to a parcel sent via this carrier from the facility.

Concept: Custom Carrier

Apart from the provided carrier connection to many "mainstream" carriers fulfillmenttools offers another flavor of carriers: The so called Custom Carrier. Those represent a carrier that is not natively supported by fulfillmenttools but is still used in the field.

Some examples for a custom carrier setup could be

  • Sending parcels via a local bike carrier, that is called by phone or messenger or whatever when needed

  • the shop owner, who takes parcels to a local carrier pickup point whenever it suits the operational process (maybe on his way home after work)

  • an own carrier fleet that delivers goods on its own schedule using detached tooling (like a standalone routing service & delivery app)

It is possible to create several custom carriers. Since the carrier key must be unique a custom carrier must have the key starting with "CUSTOM" followed by any appendix (e.g. CUSTOM_FORWARDINGAGENCY, CUSTOM_COURIER,...)

Available Carriers

Currently fulfillmenttools supports the following carriers. Please note, that this list is constantly extended. Please get in contact with us in case you need a specific not yet implemented carrier.

Standard Carriers

CarrierRelease StateKeyNotes

BPOST

Within Belgium

GA

BRING

Within Norway

GA

DHL_V2

Within Germany

DHL_BENELUX

Within Belgium, Netherlands, Luxembourg

DHL_EXPRESS

DPD

GA

DPD_CH

Only for shipments originating in Switzerland.

GA

FEDEX

Only for shipments originating in Germany.

FEDEX_ECO

GA

GLS

For PUDO delivery the Parcel Locker ID must be set in the field "additional address information".

La Poste / Asendia

GA

LA_POSTE

Within France

GA

POST_AT

Within Austria

POST_CH

Within Switzerland

GA

POSTNL

Within the Netherlands

GA

POST_NORD

Scandinavian countries

UPS

worldwide

Sameday Carriers

CarrierRelease StateKeyNotes

GA

ANGEL

Only from certain metropolitan areas within Germany to specific target postal codes (depending on originating metro area).

Sameday - what does this mean?

The idea of sending goods not only from a centralized warehouse but from a brick and mortar store next to the customer opens up more possibilities to offer services like Same Day delivery or event instant delivery.

Fulfillmenttools supports this by offering an endpoint which allows a checkup for delivery options like Same Day within the customer’s checkout process in the shop system.

Our fulfillment options endpoint (see: fulfillment options) is able to check for Same Day options within the network on the basis of the input parameters: estimated order date, address of the customer, service level (best effort e.g. sfs or Same Day) and (optional) the basket of the customer (ordered items).

The check takes into account whether

  • a facility exists which has the service type ship-from-store and at least one Same Day carrier

  • the Same Day carrier delivers into the area the customer lives in

  • there is enough time to fulfill and deliver the goods to the customer. This is done by verify whether the process (process time can be set - we call this fulfillment buffer) can be performed within the fulfillment times and is finished before the carrier picks up the order.

  • optionally: the ordered articles are in stock (our offlinestock as well as reserved stock are taken into consideration)

If there is a positive Same Day check-up result our endpoint communicates this and the shop can now allow the customer to select this option. We make this option only valid for a certain timespan, otherwise the temporal circumstances changes and Same Day delivery might not be possible anymore.

The “valid until” time is calculated like this:

cut off time of the selected carrier - fulfillment time

In case multiple facilities are available, the one with the youngest carrier cutoff time is chosen so that there might be some time left in case a reroute is necessary.

We make sure that this check-up is performed just within seconds, since the customer should not wait for the options to select.

Connect an existing carrier to a facility

Let's assume that we have a facility (see Add and Manage Facilities) and a carrier (see Creation of a carrier) already created. Lets receive the correspondent entities first (omitting some of the attributes):

The Carrier

curl -sSL 'https://your.api.fulfillmenttools.com/api/carriers/488bd1a6-6c76-4407-8dab-ece716dac14d' \
  --header 'Authorization: Bearer <TOKEN>'

200 OK
{
    "id": "488bd1a6-6c76-4407-8dab-ece716dac14d",
    "key": "DHL_V2",
    "name": "DHL",
    "status": "ACTIVE",
    "lifecycle": "GA",
    "deliveryType": "DELIVERY",
    "version": 11,
    "created": "2022-10-19T10:02:03.150Z",
    "lastModified": "2023-03-29T08:44:51.484Z",
    "credentials": {
        "key": "DHL_V2"
    }
    ...
}

The Facility

curl -sSL 'https://your.api.fulfillmenttools.com/api/facilities/a81a1c85-cfe5-49d3-81d3-675154ef323e' \
  --header 'Authorization: Bearer <TOKEN>'

200 OK
{
    "id": "a81a1c85-cfe5-49d3-81d3-675154ef323e",
    "name": "Darkstore Cologne",
    "address": {
        "street": "Schanzenstr.",
        "houseNumber": "8-20",
        "postalCode": "51063",
        "city": "Cologne",
        "country": "DE",
        "companyName": "fulfillmenttools",
        "resolvedCoordinates": {
            "lon": 7.013988,
            "lat": 50.964969
        },
        "resolvedTimeZone": {
            "offsetInSeconds": 3600,
            "timeZoneId": "Europe/Berlin",
            "timeZoneName": "W. Europe Standard Time"
        },
        ...
    },
    "locationType": "WAREHOUSE",
    "services": [
        {
            "type": "PICKUP"
        },
        {
            "type": "SHIP_FROM_STORE"
        }
    ],
    "status": "ONLINE",
    "created": "2022-12-20T10:00:48.419Z",
    "lastModified": "2023-03-27T14:13:51.213Z",
    "version": 6,
    ...
}

Please take note of two important parameters we are going to use in the following: The id of the facility (a81a1c85-cfe5-49d3-81d3-675154ef323e in this case) and the id of the carrier (488bd1a6-6c76-4407-8dab-ece716dac14d). We need both values in order to create a connection between those two entities.

The Connection

By using both values in our target URL the following call we configure the carrier to said facility:

curl -sSL -X POST 'https://your.api.fulfillmenttools.com/api/facilities/a81a1c85-cfe5-49d3-81d3-675154ef323e/carriers/488bd1a6-6c76-4407-8dab-ece716dac14d' \
--header 'Authorization: Bearer <TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "status": "ACTIVE",
    "cutoffTime": {
        "hour": 16,
        "minute": 30
    }
}'

200 OK
{
    "version": 1,
    "name": "DHL_V2",
    "status": "ACTIVE",
    "cutoffTime": {
        "hour": 16,
        "minute": 30
    },
    "carrierRef": "488bd1a6-6c76-4407-8dab-ece716dac14d",
    "key": "DHL_V2",
    "deliveryType": "DELIVERY",
    "deliveryAreas": [],
    "facilityRef": "a81a1c85-cfe5-49d3-81d3-675154ef323e",
    "id": "a81a1c85-cfe5-49d3-81d3-675154ef323e_488bd1a6-6c76-4407-8dab-ece716dac14d",
    "created": "2023-04-13T12:57:46.254Z",
    "lastModified": "2023-04-13T12:57:46.254Z"
}

The empty array of the attribute deliveryAreas indicates, that there are no restrictions regarding delivery targets.

We also conveniently set the cutoff time to 16:30, because in our fictive case this is the time when the DHL driver typically arrives at the Darkstore to pick up the parcels.

That's it: As long as the Carrier and the FacilityCarrierConnection stays in status "ACTIVE", it should be possible to create shipments and in the end also parcels using DHL as a CEP (see Create a shipment with parcels to track) originating from this facility.

Create a shipment with parcels to track

Before we dive into the process of sending a parcel we need to clarify two more entities: Shipment and Parcel.

EntityDescription

Parcel

A parcel is the digital representation of one physical parcel that is avised / on its way to the consumer.

Shipment

A shipment is the bracket around all parcels sent within a process using the same carrier. Please note: Certain data could vary from parcel to parcel: dimensions or targetAddress of the shipment can be overwritten in a parcel instance.

Let's assume we already connected a Facility with a Carrier using a FacilityCarrierConnection (see Connect an existing carrier to a facility). In this use case we are going to create a standalone shipment and two parcels - without having a Pickjob or any other leading entity. This is a perfect example on how flexible fulfillmenttools is: There might be standard processes, but you are also able to use parts of the system to suit your operational needs - like for example the need of dynamically create a Shipment Label for further use in other systems.

Create Shipment for pickup in a facility and by a carrier

With the following call we create a shipment to be picked up in facility with ID 4fd70a59-9efe-40fd-808b-08ae86ca558f using the DHL Carrier with ID 488bd1a6-6c76-4407-8dab-ece716dac14d:

curl -sSL -X POST 'https://your.api.fulfillmenttools.com/api/shipments' \
--header 'Authorization: Bearer <TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "facilityRef":"4fd70a59-9efe-40fd-808b-08ae86ca558f",
    "carrierRef": "488bd1a6-6c76-4407-8dab-ece716dac14d",
    "orderDate":"2023-04-13T13:58:46.254Z",
    "targetAddress":{
        "firstName":"Luke",
        "lastName":"Skywalker",
        "street":"Planetenstraße",
        "houseNumber":"13",
        "postalCode":"40223",
        "city":"Düsseldorf",
        "country":"DE"
    },
    "targetTime":"2023-04-13T13:58:46.254Z"
}
'

201 Created
{
    "facilityRef": "4fd70a59-9efe-40fd-808b-08ae86ca558f",
    "carrierRef": "488bd1a6-6c76-4407-8dab-ece716dac14d",
    "orderDate": "2023-04-13T13:58:46.254Z",
    "targetAddress": {
        "firstName": "Luke",
        "lastName": "Skywalker",
        "street": "Planetenstraße",
        "houseNumber": "13",
        "postalCode": "40223",
        "city": "Düsseldorf",
        "country": "DE"
    },
    "targetTime": "2023-04-13T13:58:46.254Z",
    "id": "3e62db41-af9e-4ce1-8c44-1ef340457057",
    "hasActiveCarrier":true
    "status": "INITIAL",
    "_order": "0",
    "processId": "3af2e1a7-d104-4ef9-a43b-13890ae318cf",
    "created": "2023-04-13T13:20:27.282Z",
    "lastModified": "2023-04-13T13:20:27.282Z",
    "version": 1
}

As you can see: The shipment has knowledge about the facility, the CEP and the address where to deliver.

As mentioned above: This shipment is not connected to any existing process! If you want to create a shipment to a specific process you need to provide the attribute processId within the request. Also shipments can be created during preceding processes. In that case data like the target address is provided via the order or the pickjob to the shipment which is created by fulfillmenttools.

Having the Shipment in place we are now ready to create a parcel via this shipment by issuing the following request:

curl -sSL -X POST 'https://your.api.fulfillmenttools.com/api/shipments/3e62db41-af9e-4ce1-8c44-1ef340457057/parcels' \
  --header 'Authorization: Bearer <TOKEN>'

201 Created
{
    "loadUnitRefs": [],
    "version": 1,
    "id": "713b9cd8-6d47-45ff-a12a-ef3acb270180",
    "status": "OPEN",
    "shipmentRef": "3e62db41-af9e-4ce1-8c44-1ef340457057",
    "processRef": "3af2e1a7-d104-4ef9-a43b-13890ae318cf",
    "carrierRef": "488bd1a6-6c76-4407-8dab-ece716dac14d",
    "recipient": {
        "firstName": "Luke",
        "lastName": "Skywalker",
        "street": "Planetenstraße",
        "houseNumber": "13",
        "postalCode": "40223",
        "city": "Düsseldorf",
        "country": "DE"
    },
    "sender": {
        "street": "Carlsplatz",
        "houseNumber": "3",
        "postalCode": "40213",
        "city": "Düsseldorf",
        "country": "DE",
        "companyName": "ptdus",
        "resolvedCoordinates": {
            "lon": 6.77285093516085,
            "lat": 51.2263665950437
        },
        "emailAddresses": null,
        "additionalAddressInfo": null,
        "phoneNumbers": null,
        "resolvedTimeZone": {
            "offsetInSeconds": 3600,
            "timeZoneId": "Europe/Berlin",
            "timeZoneName": "W. Europe Standard Time"
        }
    },
    "dimensions": {
        "weight": 2000
    },
    "created": "2023-04-14T15:03:07.982Z",
    "lastModified": "2023-04-14T15:03:07.982Z"
}

Please take note that we are issuing a parcel the most simple way in which fulfillmenttools will use all necessary information from the corresponding shipment. However, you are also able to overwrite certain data from the shipment for each created parcel, such as the dimensions, the recipient, the sender or return address.

We just created the entity that represents the parcel in status OPEN. Now, in an async matter, fulfillmenttools is automatically taking care of the actual advising of the parcel label. During the time it takes fulfillmenttools and the CEPs API to negotiate and create a parcel, the status of the parcel changes from OPEN via PROCESSING to finally DONE. The shipment also transparently changes its status to CONFIRMED as soon as all created parcels of the shipment reached the status DONE.

Interpreting the result of a parcel request

Once the parcel reached one of the status DONE (happy case) or FAILED it is time to take a look at the result attribute of the parcel:

curl -sSL 'https://your.api.fulfillmenttools.com/api/parcels/713b9cd8-6d47-45ff-a12a-ef3acb270180' \
  --header 'Authorization: Bearer <TOKEN>'

200 OK
{
    "loadUnitRefs": [],
    "version": 3,
    "id": "713b9cd8-6d47-45ff-a12a-ef3acb270180",
    "status": "DONE",
    "shipmentRef": "3e62db41-af9e-4ce1-8c44-1ef340457057",
    "processRef": "3af2e1a7-d104-4ef9-a43b-13890ae318cf",
    "carrierRef": "488bd1a6-6c76-4407-8dab-ece716dac14d",
    "result": {
        "summary": "",
        "carrierTrackingNumber": "222201010030668852",
        "trackingUrl": "https://nolp.dhl.de/nextt-online-public/set_identcodes.do?lang=de&idc=222201010030668852",
        "sendLabelUrl": "https://storage.googleapis.com/ocff-greycat-git-shipment-label-bucket/713b9cd8-6d47-45ff-a12a-ef3acb270180_send.pdf",
        "labelUrl":"https://storage.googleapis.com/ocff-greycat-git-shipment-label-bucket/713b9cd8-6d47-45ff-a12a-ef3acb270180_all.pdf"
    },
    ...
}

The result in general shows details about the outcome of the request:

  • In case of a successful termination of the request towards this specific CEP API (see above) we can find the trackingNumber, a tracking URL, a label to download the sendLabel and also with the attribute labelUrl a document that consists of all the documents that are of importance to this parcel (such as for example deliveryNotes, return labels, etc.).

  • In case of a unsuccessful termination of the request the summary attribute carries information about the root cause of the failure. The contents of this error message usually originates from the carrier - fulfillmenttools has limited control about the data.

In this case we are ready to download the avised label using the following call:

curl -sSL 'https://your.api.fulfillmenttools.com/api/parcels/713b9cd8-6d47-45ff-a12a-ef3acb270180/labels/all.pdf' \
  --header 'Authorization: Bearer <TOKEN>'

200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename=all.pdf

<binary data>

Be aware that the response has the Content-Type application/pdf and contains binary data.

Last updated