Overview

Important concepts can be found in this section as well as detailed information on the API endpoints.

Base URL

All URL paths referenced in this document are relative to the following base URL

API Base URL
PFLlink API https://pflapi.com/api/v1

Available Objects and Methods

The PFLlink API exposes the following objects. In general, all objects are contextualized to the User identified by the Bearer token.

API Reference and Console

For full details of the REST endpoints and their supported operations, see the API Reference at the PFLlink Developer Portal

NOTE:
You must be a registered developer and subscribed to the API to send requests through the interactive API test console.

Swagger File

To view the PFLlink API using the Swagger UI explorer, visit http://petstore.swagger.io/?url=https://pflapi.com/meta/v1/swagger

The Swagger UI explorer offers better documentation on the request and response models used by the API, but it doesn’t allow for you to test calls against the API at this time.

The (production) Swagger 2.0 metadata file can be downloaded from the following URL:

Verbose Flag

A handful of the API requests return a subset of available object data by default. By passing an optional GET parameter you can request that full object details be returned. To get full object details pass a GET parameter with a key of ‘verbose’ and a value of ‘true’.

By default, this is set to ‘false’. In general, the default response is enough to generate a user interface but there may be cases where more information is needed. Be aware that setting the verbose flag to ‘true’ will cause the response to be much slower. Use this feature only where appropriate.

The verbose flag is available on the following functions :

General Status Codes

This table provides a list of the status codes that can be returned on API calls and a brief description of each.

Code Response Notes
200 OK Success state that returns a response payload. If there is no content associated with the response, a 204 status code will typically be returned instead.
201 Created Success state that generally returns a populated ID in addition to the original information entered.
204 No Content Success state that does not have a payload.
400 Bad Request Failure state that does return a response body. (Details of what is returned are shown below.)
401 Unauthorized (Invalid) Failure state that does not return a response body. This code is typically returned due to a missing auth token, but can also be returned if access for that particular request is not allowed.
404 Does not exist or is not available Standard ‘Could Not Be Found’ failure state. This does not return a response body.

400 Status Code Response Details

{
        "Message": "The request is invalid.",
        "ModelState": {
        "application.Name": ["The Name field is required."]
  }
}
\\ The model state returned in a 400 error can have multiple properties and multiple failures per property.

Application Endpoints

Applications can be either first-party integrations (with applications like Marketo or ExactTarget) or custom applications created in the PFL Portal. Applications generally grant users access to their PFL stores and products from the perspective of a marketing campaign. Events in an application can be used to trigger specific, targeted orders.

Operation HTTP Method Path Description
Icon GET /application/{applicationID}/icon Gets the icon of an application
Set Icon POST /application/{applicationID}/icon Sets an icon for the application
Create POST /application/{applicationID}/key Creates a new key for an existing application
Regenerate Key PATCH /application/{applicationID}/key/{keyID}/regenerate Creates a new key based off an existing one
Remove Key DELETE /application/{applicationID}/key/{keyID} Deletes an existing key
Read List GET /application/{applicationID}/user Returns either all user roles associated with the specified app or only the roles associated with the user, depending on permissions
Read List GET /application/{applicationID}/user/{userID} With sufficient permissions, this returns all roles associated with the specified app and user otherwise it returns access denied
Associate to User POST /application/{applicationID}/user Associates a user to an application and grants them a specified role
Remove Permissions DELETE /application/{applicationID}/user Removes all permissions for the current user, however, the ‘admin’ privilege can only be removed if another user has been granted admin rights
Remove Permissions DELETE /application/{applicationID}/user/{userID} Removes all permissions for the specified user, however, the ‘admin’ privilege can only be removed if another user has been granted admin
Remove Permission DELETE /application/{applicationID}/user/{userID}/role/{roleID} Removes a single permission for the specified user
Associate to Store POST /application/{applicationID}/store Associates an application with a store
Remove Association DELETE /application/{applicationID}/store/{storeID} Removes an association from an application to a store
Read List GET /application/{applicationID}/store Gets all stores associated with an application
Update PATCH /application/{applicationID} Updates an existing application
Create POST /application Createa a new application. The current user will get ‘admin’ for the newly created application automatically.
Read List GET /application Lists all applications that the current user has access to
Read Details GET /application/{applicationID} Returns complete details for a single application
Enable/Disable DELETE /application/{applicationID} Enables or disables an application

EDDM Delivery Endpoints

The EDDM delivery endpoints return data on the number of residents and businesses within a certain postal area (population details).

Operation HTTP Method Path Description
Read List GET /delivery/eddm Fetches the population for a set of postal codes. The list of postal codes cannot exceed 100 or a 404 error will be returned.
Read Detail GET /delivery/eddm/{postalCode} Fetches the population for a specific postal code
Read Detail GET /delivery/eddm/{postalCode}/route/{carrierRouteID} Fetches the population of a specific route

Meta Endpoints

The meta collection of endpoints provides information about the different lookup types/enums used by the PFLlink API.

Operation HTTP Method Path Description
Read List GET /meta/permissions Fetches a list of acceptable permissions and special roles
Read Detail GET /meta/permissions/{special} Returns the underlying permissions associated with a special role name
Read List GET /meta/orderstatus Fetches a list of order statuses
Read List GET /meta/paymentMethod Returns a list available payment methods
Read Detail GET /meta/about Returns version information about the API

Order Endpoints

When a Product is sent, an Order is created. As the Order moves through the fulfillment lifecycle, the Order object is updated with the current status and relevant tracking numbers. The order endpoints also contain information on the user who placed the order, who the recipient is, and payment details. (Order Status events can optionally be broadcast out to a configurable endpoint. See Order Status Events.)

Operation HTTP Method Path Description
Price Quote POST /store/{storeID}/order/price Get a price quote for a batch (or single) order
Status GET /store/{storeID}/order/{orderID}/status Get order status history for a single order
Read List GET /store/{storeID}/order Get a list of orders that have been submitted by the current user
Status GET /store/{storeID}/order/{orderID} Get a single order status
Create POST /store/{storeID}/order Create a batch (or single) order
Update PATCH /store/{storeID}/order/{orderID} Update an existing order that is in a recoverable (3000) status. Can only be used when the order is in this state. Null fields in the request will be replaced with their prior values

Create Order Example

Below is an example of creating a minimal order.

REQUEST
Method: POST
URL: APIRoot/store/123456/order


        {
        "Items": [
        {
            "Products": [{"TemplateFields":{},"DeliveryMethod":"FDXG","ID":"9876","Quantity":1}],
            "Recipients": [{
            "Email": "Regression.Malcolm@45n.co",
            "Title": "Captain",
            "NameFirst": "Malcolm",
            "NameLast": "Reynolds",
            "CompanyName": "Firefly Enterprises",
            "AddressLine1": "100 Junker Lane",
            "AddressLine2": "",
            "AddressLine3": "",
            "City": "Standardville",
            "Province": "MT",
            "Postalcode": "59718",
            "Country": "US",
            "BusinessPhone": "406-555-5555",
            "MobilePhone": ""
        }]
        }
        ],
            "MaxRetries": 1,
            "TestMode": true
        }
    
    

RESPONSE
Status Code: 200


        [{
            "ID": "91ff4512-8fa7-42d7-a472-cbdccea8a35b",
            "StatusDescription": "Order Request Received",
            "StatusID": 1000,
            "Completed": false,
            "OrderNumber": null,
            "StoreID": "123456",
            "Created": "2016-09-22T15:55:03.8738708Z",
            "Updated": null,
            "OrderDetails": null,
            "_meta": {
                "Messages": []
            }
        }]
            

Order Details Example

Below is a request for order details containing high level information about what status it is and what the order that created it looked like.

REQUEST
Method: GET
URL: APIRoot/store/storeID/order/orderID

RESPONSE
Status Code: 200


        {
            "ID": "*Order ID*",
            "StatusDescription": "Order Placed",
            "StatusID": 2000,
            "Completed": false,
            "OrderNumber": "*Order Number*",
            "StoreID": "*Store ID*",
            "Created": "2016-09-27T20:11:22.143",
            "Updated": null,
            "OrderDetails": {
            "Products": [{
            "DeliveryMethod": "FDXG",
            "ID": "9895",
            "Quantity": 1
        }],
        "Recipient": {
            "Email": "Regression.Malcolm@45n.co",
            "Title": "Captain",
            "NameFirst": "Malcolm",
            "NameLast": "Reynolds",
            "CompanyName": "Firefly Enterprises",
            "AddressLine1": "100 Junker Lane",
            "City": "Standardville",
            "Province": "MT",
            "Postalcode": "59718",
            "Country": "US",
            "BusinessPhone": "406-624-9655",
            "MobilePhone": ""
        }
        },
            "_meta": {
                "Messages": []
            }
        }
      
        

Order History Example

Below is an example of retrieving order status history.

REQUEST
Method: GET
URL: APIRoot/store/storeID/order/orderID/status (use ?latest=true to get just the most recent status)

RESPONSE
Status Code: 200


        [{
            "ProductID": "9895",
            "OrderID": "ORDER ID",
            "OrderNumber": "B11908540651",
            "Timestamp": "2016-09-27T20:12:22.603",
            "StatusID": 2000,
            "StatusDescription": "Order Placed",
            "_meta": {
                "Messages": ["New Order Placed: B11908540651"]
            }
        }, 
        
        {
            "OrderID": "32262355-de3d-42af-9f48-b1b8ddb46ddb",
            "OrderNumber": "B11908540651",
            "Timestamp": "2016-09-27T20:11:22.143",
            "StatusID": 1000,
            "StatusDescription": "Order Request Received"
        }]
            

Order Billing Variables

The order endpoint payloads can contain billing variables. Billing variables are simply a key / value pair that will be added to the PFL order. Think of them as user-specified meta data; for example, a user might want to include the static value 'Marketing Department : Q1 Budget' on their invoices.

Requirements

  • Both a Key and a Value are required
  • There is no hard limit to the number of Key/Value pairs that can be added to an order
  • Keys and Values are each limited to 255 characters

Below is the payload for the GET /order endpoint for reference on how billing variables are formatted.


  {
  "PageIndex": 0,
  "PageSize": 0,
  "TotalCount": 0,
  "TotalPageCount": 0,
  "HasNextPage": true,
  "HasPrevPage": true,
  "Items": [
    {
      "PartnerOrderReference": "string",
      "ID": "string",
      "StatusDescription": "string",
      "StatusID": 0,
      "Completed": true,
      "OrderNumber": "string",
      "StoreID": "string",
      "Created": "2016-08-18T16:27:22.723Z",
      "Updated": "2016-08-18T16:27:22.723Z",
      "OrderDetails": {
        "Product": {
          "TemplateFields": {},
          "DeliveryMethod": "string",
          "IntelligentMailingSerialNumber": "string",
          "ID": "string",
          "Quantity": 0,
          "FileURL": "string",
          "ProductionSpeedDays": 0
        },
        "Products": [
          {
            "TemplateFields": {},
            "DeliveryMethod": "string",
            "IntelligentMailingSerialNumber": "string",
            "ID": "string",
            "Quantity": 0,
            "FileURL": "string",
            "ProductionSpeedDays": 0
          }
        ],
        "Recipient": {
          "Email": "string",
          "Title": "string",
          "NameFirst": "string",
          "NameLast": "string",
          "CompanyName": "string",
          "AddressLine1": "string",
          "AddressLine2": "string",
          "AddressLine3": "string",
          "City": "string",
          "Province": "string",
          "Postalcode": "string",
          "Country": "string",
          "BusinessPhone": "string",
          "MobilePhone": "string"
        },
        "EddmDeliveryAreas": [
          {
            "PostalCode": "string",
            "CarrierRoute": "string",
            "TargetAudience": "All"
          }
        ],
        "Customer": {
          "NameFirst": "string",
          "NameLast": "string",
          "CompanyName": "string",
          "AddressLine1": "string",
          "AddressLine2": "string",
          "City": "string",
          "Province": "string",
          "PostalCode": "string",
          "Country": "string",
          "Email": "string",
          "Phone": "string"
        },
        "Payments": [
          {
            "ExpectedAmount": 0,
            "ID": "string",
            "Method": "stripe"
          }
        ],
        "PartnerOrderReference": "string",
        "StatusCallbackUrl": "string",
        "StatusDetailsCallbackUrl": "string",
        "BillingVariables": [
        {
        "key": "string",
        "value": "string"
        }
        ]
      },
      "_meta": {
        "Messages": [
          "string"
        ],
        "ChangeType": "Created"
      }
    }
  ]
}

Multiple Recipients Per Order

The GAPI order endpoints support the ability to have multiple recipients per order in which each recipient can receive all or part of the specified products. For example, this functionality makes it possible for 50 people to all get one item or for a certain 25 to get one thing and the other 25 to get another thing (and the 5 magic people in the middle would get both).

Sample Use Cases

  • Alice, Barb and Claire can each receive 1 coffee cup and 2 pens (many to many)
  • A and B each receive a coffee cup and both C and D get 2 pens (many to one & many to one)
  • A, B & C receive cups, but only C gets pens (many to many and one to many)
  • A gets 1 cup and 2 pens (one to many)

Requirements / Notes

  • If one recipient has a file URL specified then all recipients must.
  • FileURL can be specified at the product level to have it effect all recipients. FileURL specified in the limitToRecipients array will then override the product level field.
  • All RecipientKeys specified must be unique to the order.
  • All addresses must meet PFL API validation standards or the call will fail.
  • Ordering the same product twice in a single order is not allowed for any reason (the most common example of this would be differing templates of the same product for different people).
  • Because multiple recipients per order are treated as a mailing list, multiple delivery methods are not allowed.
  • Only certain delivery methods (USPS and FedEx) are supported for multiple recipient orders. The codes that can be used are listed in the table below.

Available Shipping Methods

Code Description
1C 1st Class
1CP 1st Class - Presorted
BM Bulk Mail

Below is a sample payload for the structure of a multiple recipient order. The Quantity field will be computed automatically so it is not recommended that this ever be set manually when using multiple-recipients.


{
    "Items": [{
        "Products": [{
            "DeliveryMethod": "string",
            "ID": "asdf",
            "Quantity": 302,
            "LimitToRecipients": [{
                "RecipientKey": "1234",
                "Quantity": 2
            }, {
                "RecipientKey": "5678",
                "Quantity": 300
            }]
        }, {
            "DeliveryMethod": "string",
            "ID": "xkcd",
            "Quantity": 2,
            "LimitToRecipients": [{
                "RecipientKey": "1234",
                "Quantity": 1
            }, {
                "RecipientKey": "5678",
                "Quantity": 1
            }]
        }, {
            "DeliveryMethod": "USPS",
            "ID": "qwert",
            "Quantity": 3,
            "QuantityPerRecipient": 1
        }],
        "Recipients": [{
            "RecipientKey": "5678",
            "NameFirst": "string",
            "NameLast": "string",
            "AddressLine1": "string"
        }, {
            "RecipientKey": "1234",
            "NameFirst": "string",
            "NameLast": "string",
            "AddressLine1": "string"
        }, {
            "NameFirst": "string",
            "NameLast": "string",
            "AddressLine1": "string"
        }],
    }],
    "MaxRetries": 0,
    "TestMode": true
}

The POST /store/{storeID}/order endpoint allows a customer to pay for an order with a credit card by including a Payments property in the payload. One important nuance of the Payments property is that it contains an ExpectedAmount field. The ExpectedAmount field acts as a sanity check for orders that may have a different negotiated price than final price. It is possible to leave this field empty and have the value default to the complete order value. It does not effect what is actually charged. Any value entered in the ExpectedAmount field will be cross-checked against the cost of the order. If a discrepancy between the two is found, it will halt the order until the customer and PFL resolve the different.

The Payment property can be added to the POST /store/{storeID}/order endpoint to allow the customer to pay using a credit card. It is an optional property. A sample payload that includes the Payments property is shown below. Adding the Payments property with the appropriate values will allow the credit card information to be processed and charged.


{
  "Items": [
    {
      "Product": {
        "TemplateFields": {},
        "DeliveryMethod": "string",
        "ID": "string",
        "Quantity": 0,
        "FileURL": "string"
      },
      "Products": [
        {
          "TemplateFields": {},
          "DeliveryMethod": "string",
          "ID": "string",
          "Quantity": 0,
          "FileURL": "string"
        }
      ],
      "Recipient": {
        "Email": "string",
        "Title": "string",
        "NameFirst": "string",
        "NameLast": "string",
        "CompanyName": "string",
        "AddressLine1": "string",
        "AddressLine2": "string",
        "AddressLine3": "string",
        "City": "string",
        "Province": "string",
        "Postalcode": "string",
        "Country": "string",
        "BusinessPhone": "string",
        "MobilePhone": "string"
      },
        "Payments": [
        {
        "ExpectedAmount": 0,
        "ID": "string",
        "Method": "stripe"
        }
      ],
      "PartnerOrderReference": "string",
      "StatusCallbackUrl": "string"
    }
  ],
  "MaxRetries": 0,
  "TestMode": true
}
        

The following notes contain details on the three fields included in the Payments property and their particular quirks:

ExpectedAmount

This is not a required field and should only be used if there is a chance that the negotiated value of the order and the actual price of the order may differ (as may be the case when the actual order size is ambivalent). Adding a value here will cross-check this value against the calculated actual cost of the order. If the two are the same, the order will proceed as expected. If the two differ, the order will be halted until the customer and PFL have a chance to sort out the difference. If no value is entered, it will default to null and the order will proceed as expected, using the cost of the order as the amount to be charged to the credit card.

ID

The ID is a single use token returned by the Stripe API. This is not the credit card number. More details on how the ID is generated from Stripe can be found here.

Method

PFL uses Stripe APIs for credit card payments. Information on the how the Stripe APIs work can be found here and it is important to note that the Payment property currently only accepts the Stripe Production White Label public token.

Order Status Event Codes

As an order goes through all its stages from beginning to end, there are various status events that indicate its progress.

Status Event Codes

1000 : Order Request Recieved
A 1000 status code indicates that the order request has been successfully received by PrintingForLess and is being processed. An OrderID is generated that can be used to reference the order. At this point the OrderNumber does not yet exist. (Refer to the table at the bottom of this page for more details on OrderID and OrderNumber.)
From here, the order will either progress to a 2000 status (indicating the order was successfully placed), a 3000 status (indicating there was an error with the order’s information) or a 5000 status (indicating the order request failed for an unknown reason). Common errors that would trigger a 3000 status are an incorrect shipping method––like specifying FedEx details for an order that is set up to be shipped through the Postal Service––or an invalid quantity ––like a negative number or ordering more than the available number of products.
2000 : Order Placed
A 2000 status code indicates that the order has been successfully processed by PrintingForLess and is now in progress. From here, the order will sequentially progress through the remaining stages of production and shipping until the customer has received the product(s) unless someone cancels the order.
There is a callback URL with each order that can potentially result in communication failures between the PFL API and the associated application. This simply means that it is possible for the order to successfully go to the next stage of production or delivery but not correctly in the application.
Callback Success and Failure Example
If order status updates stop showing up in the client application, it is likely this occurred. Automatic retries to communicate the correct status to the application occur in 60 minute increments and will retry 3 times. Any callback communication failures between the PFL API and the application can be viewed in the order history.
2100 : Order Entered Production
A 2100 status code indicates the order is actively being printed, packaged or prepared for shipping.
2200 : Order Shipped
A 2200 status code indicates the order has shipped and (if a tracking number has been provided) can be monitored. Depending on the carrier, it is possible for this to be the final status of the order.
2300 : Order Delivered
A 2300 status code indicates the order has been successfully delivered. This is likely to be the final status of the order, although some deliveries will also include a ‘picked up’ status (2400).
2400 : Order Picked Up
A 2400 status code indicates the order has been successfully picked up. This would typically apply in a situation where the recipient has to retrieve the order from a location like a FedEx center. This is the final status of the order.
2500 : Order Cancelled
A 2500 status code indicates that PrintingForLess has cancelled the order. In all likelihood this event was triggered by a customer placing a phone call to PFL stating they would like to cancel it. Details about when the order was cancelled will be available in the order history.
3000 : Recoverable Error Occurred
A 3000 status code indicates that something went wrong when the order was placed and it was rejected due to an error in the order’s information. The status history will contain appropriate information on what caused the failure. Common mistakes to look for are incorrect shipping methods––like specifying FedEx details for an order that is set up to be shipped through the Postal Service––or invalid quantities––like a negative number or ordering more than the available number of products.
5000 : Failed to Place Order
A 5000 status indicates a critical failure occurred during the processing stage. Resubmit the order.
5300 : Failed to Deliver Order
In the unlikely event that an order failed to deliver, the carrier may have details about what went wrong.

Product Endpoints

Products are contained inside of a Store. Products are sendable items. Some printed products contain variable data that can be supplied at order time; examples include a personalized salutation on a brochure or postcard.

Operation HTTP Method Path Description
Create POST /store/{storeID}/product/{productID}/proof Generate proof URLs for products with template data
Read List GET /store/{storeID}/product Get all products for the specified store
Read Detail GET /store/{storeID}/product/{productID} Get a single product from the specified store. This always returns the full (non-sparse) details

Get Products Example

Below is an example of a request for available products.

REQUEST
Method: GET
URL: APIRoot/store/storeID/product (use ?verbose=true to get template field values)

RESPONSE
Status Code: 200


        [{
            "StoreID": "*STORE ID*",
            "ID": "9876",
            "DefaultQty": 1,
            "MinimumQty": 1,
            "MaximumQty": null,
            "IncrementQty": 1,
            "Name": "Postcards 4x6 Color/Color",
            "Description": "",
            "ImageURL": "//sw-public.pflnet.net/tosthumbnails/lobapi/lob_postcard.jpg",
            "HasTemplate": false,
            "DeliveryMethod": "FDXG",
            "TemplateFields": null,
            "DeliveredPrices": [{
            "DeliveryMethodCode": "FDXG",
            "Description": "FedEx Ground",
            "Price": 0.97,
            "Country": null,
            "CountryCode": null,
            "Created": "2016-09-23T11:32:28.85",
            "LocationType": "domestic",
            "IsDefault": true
        }, 
        {
            "DeliveryMethodCode": "1C",
            "Description": "1st Class",
            "Price": 26.060000000000002,
            "Country": null,
            "CountryCode": null,
            "Created": "2016-09-23T11:32:28.867",
            "LocationType": "domestic",
            "IsDefault": false
        }],
        "ProductionSpeeds": [{
            "Days": 1,
            "IsDefault": true 
            }]
        }, 
            
        {
            "StoreID": "*STORE ID*",
            "ID": "9877",
            "DefaultQty": 1,
            "MinimumQty": 1,
            "MaximumQty": null,
            "IncrementQty": 1,
            "Name": "Postcards 5x7 Color/Color",
            "Description": "",
            "ImageURL": "//sw-public.pflnet.net/tosthumbnails/pflautomation/testhangtags.jpg",
            "HasTemplate": false,
            "DeliveryMethod": "FDXG",
            "TemplateFields": null,
            "DeliveredPrices": [{
            "DeliveryMethodCode": "FDXG",
            "Description": "FedEx Ground",
            "Price": 1.23,
            "Country": null,
            "CountryCode": null,
            "Created": "2016-09-23T11:32:28.867",
            "LocationType": "domestic",
            "IsDefault": true
        }, 
            
        {
            "DeliveryMethodCode": "1C",
            "Description": "1st Class",
            "Price": 26.32,
            "Country": null,
            "CountryCode": null,
            "Created": "2016-09-23T11:32:28.867",
            "LocationType": "domestic",
            "IsDefault": false
        }],
        "ProductionSpeeds": [{
            "Days": 1,
            "IsDefault": true}]
        }]
            

Product Shipping Methods

There is one default domestic shipping method and one default international shipping method. Although that's the case and the expected behavior might be a single entry returned for each, it is possible for the API to return multiple entries for any international shipping method. Multiple entries are returned when there is more than one country because it is possible for the price to vary by country. In addition, a null entry will always be returned per type of shipping method (both international and domestic). As you can see in the code below, the five highlighted entries with are identical (i.e. the same default international shipping method) with their only variation occurring in the country and country code returned. The entries that are not highlighted are a shipping method that is not the default, but are also all the same type, Economy.


    {
        "DeliveryMethodCode": "FDXIP",
        "Description": "FedEx International Priority",
      "Price": 0.0,
      "Country": null,
      "CountryCode": null,
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
        "IsDefault": true
    },
    {
        "DeliveryMethodCode": "FDXIP",
        "Description": "FedEx International Priority",
      "Price": 0.0,
      "Country": "Australia",
      "CountryCode": "AU",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
        "IsDefault": true
    },
    {
        "DeliveryMethodCode": "FDXIP",
        "Description": "FedEx International Priority",
      "Price": 0.0,
      "Country": "Canada",
      "CountryCode": "CA",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
        "IsDefault": true
    },
    {
        "DeliveryMethodCode": "FDXIP",
        "Description": "FedEx International Priority",
      "Price": 0.0,
      "Country": "Netherlands",
      "CountryCode": "NL",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
        "IsDefault": true
    },
    {
        "DeliveryMethodCode": "FDXIP",
        "Description": "FedEx International Priority",
      "Price": 0.0,
      "Country": "Norway",
      "CountryCode": "NO",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
        "IsDefault": true
    },
    {
      "DeliveryMethodCode": "FDXIE",
      "Description": "FedEx International Economy",
      "Price": 0.0,
      "Country": null,
      "CountryCode": null,
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
      "IsDefault": false
    },
    {
      "DeliveryMethodCode": "FDXIE",
      "Description": "FedEx International Economy",
      "Price": 0.0,
      "Country": "Australia",
      "CountryCode": "AU",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
      "IsDefault": false
    },
    {
      "DeliveryMethodCode": "FDXIE",
      "Description": "FedEx International Economy",
      "Price": 0.0,
      "Country": "Canada",
      "CountryCode": "CA",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
      "IsDefault": false
    },
    {
      "DeliveryMethodCode": "FDXIE",
      "Description": "FedEx International Economy",
      "Price": 0.0,
      "Country": "Netherlands",
      "CountryCode": "NL",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
      "IsDefault": false
    },
    {
      "DeliveryMethodCode": "FDXIE",
      "Description": "FedEx International Economy",
      "Price": 0.0,
      "Country": "Norway",
      "CountryCode": "NO",
      "Created": "2016-02-08T14:34:50.067",
      "LocationType": "international",
      "IsDefault": false
    }

Product Templates

Some products can be configured to include template fields. These fields need to have populated values when an order is submitted. To check if a product has a template field look for "HasTemplate": true in the response from GET /store/{id}/product

Products that have templates will return an array of template fields:

"TemplateFields": [
      {
        "Required": false,
        "Type": "SINGLELINE",
        "SubType": "A",
        "FieldName": "FIRST_NAME",
        "Prompt": "First Name",
        "DefaultValue": "",
        "OrgValue": "PFL"
      },
      {
        "Required": false,
        "Type": "SINGLELINE",
        "SubType": "A",
        "FieldName": "EVENT_NAME",
        "Prompt": "Event Name",
        "DefaultValue": "",
        "OrgValue": "Resource Celebration"
      },
      {
        "Required": false,
        "Type": "SINGLELINE",
        "SubType": "A",
        "FieldName": "SALESPERSON",
        "Prompt": "Salesperson",
        "DefaultValue": "",
        "OrgValue": "Seth"
      }
    ],

The contents of each field should provide enough information to create a useful user interface for the field

  • Required: indicates if the field is required in the order payload
  • FieldName: the name of the field. this is the key to use with an order
  • Prompt: a user-readable name for the field. this can be used as a label in a user interface
  • DefaultValue: if nothing is entered by the user the field should revert to this value
  • OrgValue: tbd
  • Type & SubType: see below

Types & SubTypes

Template Field Types indicate the type of user interface that should be used to gather the data from the user. The types are (with indented subtypes):

  • SINGLELINE: a single line of user input
    • A: the value should be alpha (a-z) or alphanumeric (a-z,0-9) characters
    • I: the value should be an interger (0-9)
    • N: the value should be a decimal (123.12)
  • MULTILINE: multiple lines of text should be entered
  • PICKLIST: a selection should be made from a dropdown of available options
    • DROPLIST: a dropdown list should be displayed
    • LISTBOX: a listbox should be displayed
    • MULTI: a multi-select listbox should be displayed
  • GENDER: the value should indicate gender. Values are ‘male’ or ‘female’
    • M_F: possible values are ‘m’ or ‘f’
  • YESNO: a boolean input should be gathered. Values are ‘true’ or ‘false’
    • 1_0: values are ‘1’ or ‘0’
    • T_F: values are ‘T’ or ‘F’
    • Y_N: values are ‘Y’ or ‘N’
    • YES_NO: values are ‘YES’ or ‘NO’
  • GRAPHICUPLOAD: can have a url provided and the printed material will contain the image at that location
  • GRAPHICPICKLIST: an image should be selected from a list
  • SEPARATOR: a line break will be inserted at this point in the final document

Submitting an Order

Values for template fields are included in an Order by populating a Key/Value pair in the product object on the request. The Key is the FieldName value from the template definition and the value is the data gathered from the user:

{
    "Items": [{
        "Product": {
            "TemplateFields": {
                "FieldName": "Value"
            },
            "DeliveryMethod": "string",
            "ID": "string",
            "Quantity": "0"
        }
        ...
    }]
}

Generating Proofs (via URL)

Passing in key=value pairs of template fields will return a rendered versions of the product via a URL. This only works for products that have template fields. Additionally, the template cannot be completely blank (which would hopefully be an invalid use case anyway); at least one of the template fields must contain data.

{
    "FIRST_NAME": "John"
    "SALESPERSON": "Jeor Mormont"
    "EVENT_NAME": ""
}

The response payload includes the following:

Name Description Type
ProductID product ID string
Side1URL JPG URL for the first side (front) string
Side2URL JPG URL for the second side (back) string
PdfURL PDF URL for the entire document string
PageURLs JPG URLs for each page in the document collection of strings

Stats Endpoints

The stats collection of endpoints provides additional details on the user and their store interactions.

Operation HTTP Method Path Description
Read Detail GET /stats Gets stats about the current user
Read Detail GET /stats/store/{storeID} Gets stats about store orders for the current user and the specified store

Store Endpoints

A Store represents a collection of products that can be sent through the PFLlink API. Stores contain budgets, products and orders.

Operation HTTP Method Path Description
Associate to Store POST /store/invite Associate an existing user with a new PFL store
Read List GET /store/{storeID}/user Depending on permission level, this returns either a list of all user roles associated with the store or a list of the current user’s roles
Read Detail GET /store/{storeID}/user/{userID} Depending on permission level, this returns either details on other users of the store or details on the current user only
Grant Role POST /store/{storeID}/user Associate a user to a store and grant them a specified role
Remove Permissions DELETE /store/{storeID}/user Remove all permissions for the current user. Cannot be used if the current user is in the only admin role
Remove Permissions DELETE /store/{storeID}/user/{userID} Remove all permissions for a specified user. Cannot be used if the specified user is in the only admin role
Remove Permission DELETE /store/{storeID}/user/{userID}/role/{roleID} Removes a single permission for the specified user
Read List GET /store/connectedApps Fetches a list of non-generic applications available to stores the user is the admin of
Read All GET /store/{storeID}/connectedApps Finds all stores where the user is admin, then searches for (and returns) any non-generic integrations to that store. All connected apps are returned for PFL users.
Read List GET /store Returns a list of all accessible stores
Read Detail GET /store/{storeID} Returns full details of specified store

Get Store List Example

Below is an example of fetching a list of available stores.

REQUEST
Method: GET
URL: APIRoot/store (use ?verbose=true to include company names in the response payload)

RESPONSE
Status Code: 200


    [{
        "StoreID": "140158",
        "BudgetEnabled": false,
        "BudgetTypes": null,
        "BudgetDuration": null,
        "CompanyName": null
    }, 
        
    {
        "StoreID": "136085",
        "BudgetEnabled": false,
        "BudgetTypes": null,
        "BudgetDuration": null,
        "CompanyName": null
    }]
    

User Endpoints

A User represents an Identity in the PFLlink Identity Server. A User is associated to one or more Stores

Operation HTTP Method Path Description
Read GET /user/roles View all roles assigned to the current user
Read GET /user/{userID}/roles View all roles assigned to the specified user. (Applications cannot use this function.)
Create POST /user Create a new User in the PFLlink Identity Server. This method does not require a Bearer token
Enable/Disable DELETE /user/{userID} Change the enablement of a user’s required permissions (only available to PFL employees)
Read Detail GET /user Get details about the current user
Read Detail GET /user/{userID} Get details about a specific user

Permissions and Roles Overview

As a user you can have various levels of access to three things : orders, stores and applications. This section is designed to explain the available user roles and illustrate the level of access they grant. In addition to the packaged permission sets defined below (Default, Report, and Admin) which contain predefined groups of permissions, it is possible to grant an individual permission (or subset of permissions) to a user.

For example, as admin, you may want to grant only store.all.write privileges to the users of a store, allowing any of them to place orders, without giving them full rein to wreak havoc through a complete administrator permissions set. More details and helpful specifics can be found in the administrator description below.

It may be useful to note that the GET meta/permissions functions return information on available permissions and special roles.

Default (none) Permissions Set

The default (none) permissions set grants you a basic set of privileges to view orders you have placed and stores and applications you have access to. With this permission set, you also have the ability to create applications. (When you create an application, you are automatically the admin with global read / write privileges.)

Report Permissions Set

The report permissions set is similar to the default permissions set but, in addition, allows you to view details of all users of a store or application, all user roles associated with a particular store, and all orders that have been placed in your store(s).

Administrator Permissions Set

The administrator permissions set grants you global read / write access to the API endpoints. Additionally, this role can be used to assign a specific permission to a user without giving them full admin rights.

A detailed table of the specific permissions required to use each function (and conversely, the functions that each permission gives access to) can be found here. This section, in particular, will serve as your guide in determining which particular permission(s) to grant on an as-needed basis.

If you create an application, you are automatically the admin. If you are the first user granted access to a store via an invite key, you are, likewise, automatically admin. It is worth noting that a user cannot be removed from a store or an application if they are its only admin. Think of the powerlessness that would ensue.

User Roles by Function

The following table lists which user roles have access to each function.

The default permissions set and the report permissions set grant access to the same set of functions. As mentioned in the descriptions, the defining difference is that for a subset of those functions, the report set grants global read access, whereas the default set only provides details on the current user. Where the report permissions set provides a wider set of information, it has been flagged with an asterisk (*). Details on the exact information returned are available by clicking the URL.

Function URL None Report Admin
GET /application/{applicationID}/icon X X X
POST /application/{applicationID}/icon X
POST /application/{applicationID}/key X
PATCH /application/{applicationID}/key/{keyID}/regenerate X
DELETE /application/{applicationID}/key/{keyID} X
DELETE /application/{applicationID}/user X
GET /application/{applicationID}/user X X * X
POST /application/{applicationID}/user X
DELETE /application/{applicationID}/user/{userID} X
GET /application/{applicationID}/user/{userID} X X * X
DELETE /application/{applicationID}/user/{userID}/role/{roleID} X
GET /application/{applicationID}/store X
POST /application/{applicationID}/store X
DELETE /application/{applicationID}/store/{storeID} X
DELETE /application/{applicationID} X
GET /application/{applicationID} X X X
PATCH /application/{applicationID} X
GET /application X X X
POST /application X X X
GET /delivery/eddm X X X
GET /delivery/eddm/{postalCode} X X X
GET /delivery/eddm/{postalCode}/route/{carrierRouteID} X X X
GET /meta/permissions N/A N/A N/A
GET /meta/permissions/{special} N/A N/A N/A
GET /meta/about N/A N/A N/A
GET /meta/paymentMethod N/A N/A N/A
GET /meta/orderstatus NA NA NA
GET /stats X X X
GET /stats/store/{storeID} X X X
POST /store/{storeID}/order/price X
GET /store/{storeID}/order/{orderID}/status X X * X
GET /store/{storeID}/order X X * X
POST /store/{storeID}/order X
GET /store/{storeID}/order/{orderID} X X * X
PATCH /store/{storeID}/order/{orderID} X
GET /store/{storeID}/product X X X
GET /store/{storeID}/product/{productID} X X X
POST /store/{storeID}/product/{productID}/proof X X X
POST /store/invite X X X
DELETE /store/{storeID}/user X
GET /store/{storeID}/user X X * X
POST /store/{storeID}/user X
DELETE /store/{storeID}/user/{userID} X
GET /store/{storeID}/user/{userID} X X * X
DELETE /store/{storeID}/user/{userID}/role/{roleID} X
GET /store/connectedApps X
GET /store/{storeID}/connectedApps X
GET /store X X X
GET /store/{storeID} X X X
GET /user/roles X X X
GET /user X X X
POST /user X X X
DELETE /user/{userID}
GET /user/{userID} X X X

* Delineates that for this function, the report permissions set returns a wider set of information than the default permissions set. Details on the exact information returned are available by clicking the URL.

Administrator Permissions

The administrator role contains this entire list of permissions. (The default and report permissions sets (roles) each contain a specific subset of these.) The administrator can dole specific permissions from this list out as needed to grant users access to the functionality available in particular endpoints.

For example, an admin can grant one of the store permissions to a user by using the POST /store/{storeID}/user function. Similarly, the admin can grant one of the application permissions to a user with the POST /application/{applicationID}/user function.

All Permissions

store.all.read
store.all.write
store.all.delete
store.self.read
store.self.write
store.self.delete
order.all.read
order.all.write
order.all.delete
order.self.read
order.self.write
order.self.delete
application.all.read
application.all.write
application.all.delete
application.self.read
application.self.write
application.self.delete

Function URL Required Permissions
GET /application/{applicationID}/icon none : as long as an association exists between the user and app*
POST /application/{applicationID}/icon application.all.write or application.self.write
POST /application/{applicationID}/key application.all.write or application.self.write
PATCH /application/{applicationID}/key/{keyID}/regenerate application.all.write or application.self.write
DELETE /application/{applicationID}/key/{keyID} application.all.write or application.self.write
DELETE /application/{applicationID}/user application.all.write or application.self.write Not allowed if the user to be removed is the only admin
GET /application/{applicationID}/user application.all.read or application.self.read : Global details are returned for .all access. User specific details for .self access.
POST /application/{applicationID}/user application.all.write or application.self.write
DELETE /application/{applicationID}/user/{userID} application.all.write or application.self.write
GET /application/{applicationID}/user/{userID} application.all.read or application.self.read : Global details are returned for .all access. User specific details for .self access.
DELETE /application/{applicationID}/user/{userID}/role/{roleID} application.all.write or application.self.write Not allowed if the user to be removed is the only admin
GET /application/{applicationID}/store application.all.write or application.self.write and association with the specified store
POST /application/{applicationID}/store application.all.write or application.self.write and store.all.write or store.self.write
DELETE /application/{applicationID}/store/{storeID} application.all.write or application.self.write
DELETE /application/{applicationID} application.all.delete or application.self.delete : Only case in which the .delete permission is required
GET /application/{applicationID} none : as long as an association exists between the user and app*
PATCH /application/{applicationID} application.all.write or application.self.write
GET /application none : as long as an association exists between the user and app*
GET /delivery/eddm none
GET /delivery/eddm/{postalCode} none
GET /delivery/eddm/{postalCode}/route/{carrierRouteID} none
GET /meta/about N/A
GET /meta/permissions N/A
GET /meta/permissions/{special} N/A
GET /meta/orderstatus N/A
GET /meta/paymentMethod N/A
GET /stats none
GET /stats/store/{storeID} store.all.read or store.self.read
POST /store/{storeID}/order/price order.all.write or order.self.write
GET /store/{storeID}/order/{orderID}/status order.all.read or order.self.read : Global details are returned for .all access. User specific details for .self access.
GET /store/{storeID}/order order.all.read or order.self.read and association with the specified store : Global details are returned for .all access. User specific details for .self access.
POST /store/{storeID}/order order.all.write or order.self.write
GET /store/{storeID}/order/{orderID} order.all.read or order.self.read and association with the specified store : Global details are returned for .all access. User specific details for .self access.
PATCH /store/{storeID}/order/{orderID} order.all.write or order.self.write and association with the specified store : Global details are returned for .all access. User specific details for .self access.
GET /store/{storeID}/product none : as long as an association exists between the user and app*
GET /store/{storeID}/product/{productID} none : as long as an association exists between the user and app*
POST /store/{storeID}/product/{productID}/proof none : as long as an association exists between the user and app*
POST /store/invite none
DELETE /store/{storeID}/user store.all.write : store.self.write only allows you to remove yourself from the Demo store. Never allowed if the user to be removed is the only admin
GET /store/{storeID}/user store.all.read or store.self.read
POST /store/{storeID}/user store.all.write
DELETE /store/{storeID}/user/{userID} store.all.write : store.self.write only allows you to remove yourself from the Demo store. Never allowed if the user to be removed is the only admin
GET /store/{storeID}/user/{userID} store.all.read or store.self.read : Global details are returned for .all access. User specific details for .self access.
DELETE /store/{storeID}/user/{userID}/role/{roleID} store.all.write
GET /store/connectedApps must be store admin
GET /store/{storeID}/connectedApps must be store admin
GET /store none
GET /store/{storeID} none : Limited to stores you can access
GET /user/roles none
GET /user none
POST /user none
GET /user/{userID} none

* If no association exists between the two, you can request than an admin create the relationship.

Applications only need to be associated to a store if the application is going to authenticating (using client credentials rather than user credentials). In that case the user who is configuring the application must have application.*.write and store.write.

Permission Sets

Default Permissions Set

GET /application/{applicationID}/icon
GET /application/{applicationID}/user
GET /application/{applicationID}/user/{userID}
GET /application/{applicationID}
GET /application
POST /application
GET /delivery/eddm
GET /delivery/eddm/{postalCode}
GET /delivery/eddm/{postalCode}/route/{carrierRouteID}
GET /stats
GET /stats/store/{storeID}
GET /store/{storeID}/order/{orderID}/status
GET /store/{storeID}/order
GET /store/{storeID}/order/{orderID}
GET /store/{storeID}/product
GET /store/{storeID}/product/{productID}
POST /store/{storeID}/product/{productID}/proof
POST /store/invite
GET /store/{storeID}/user
GET /store/{storeID}/user/{userID}
GET /store
GET /store/{storeID}
GET /user/roles
GET /user
POST /user
GET /user/{userID}

Report Permissions Set

GET /application/{applicationID}/user
GET /application/{applicationID}/user/{userID}
GET /store/{storeID}/order/{orderID}/status
GET /store/{storeID}/order
GET /store/{storeID}/order/{orderID}
GET /store/{storeID}/user
GET /store/{storeID}/user/{userID}