API

Introduction

  • RESTful
  • Available at /api/{version}/, e.g. /api/v1/subject/method/arg. Current version: v1
  • SSL is required (except when $env['custom']['nossl'] is true - never do that in a productive environment)
  • OAuth 2 is required, endpoints are located under /oauth/{endpoint}
  • Output format: JSON (default) or XML by adding the parameter format=xml, UTF-8
  • If an error occures, the response will contain error and _errordescription strings on the top level
  • Markup in this document: required parameter, {url parameter}, error, user permission
    • Argument notation "name: type, description (default)"

OAuth 2

Every call to an API endpoint not located under /oauth requires the access_token parameter to be set.
You can use Authorization Code + Access Token (for humans) or User Credentials (for scripts) to get one. Be aware that you might get an invalid_token error after an hour, in which case you should refresh your token.

Authorization Code [GET] /oauth/authorize

Prompts the user to authorize your client to access papilio with his profile. If the user isn't logged in yet he will be promted provide his credentials as well.

Arguments

  • client_id: string, your client id
  • state: string, arbitrary string passed back to you (ensure it matches in the response)

Output

Redirects the user to the redirect URI specified in your client's settings, e.g:

http://my.app.com/authorized?code=3f3a8611a369237a7da296a5c8f717789b967b8c&state=xyz

Use this authorization code to get an access token. Or if the user has explicitly denied access you'll get:

_http://my.app.com/authorized?error=access_denied&errordescription=...&state=xyz

Access Token [POST] /oauth/token/access

Request an access token using a previously obtained authorization code.

Requires Basic Auth with your client_id as the username and the client_secret as the password.

Arguments

  • code: string, your authorization code

Output

{   "access_token": "5bb2cc6b3c8abfdee1e36d807cffc66346b896cf",
    "expires_in": 3600,
    "token_type": "Bearer",
    "refresh_token": "d872f90159216f56f9224012a3186afaab8fa258"}

or invalid_grant if the authorization code is invalid or has expired

User Credentials [POST] /oauth/token/user

Request an access token directly with a users username and password. Only recommended for scripts.

Requires Basic Auth with your client_id as the username and the client_secret as the password.

Arguments

  • username: string, your username
  • password: string, your password

Output

{   "access_token": "5bb2cc6b3c8abfdee1e36d807cffc66346b896cf",
    "expires_in": 3600,
    "token_type": "Bearer",
    "refresh_token": "d872f90159216f56f9224012a3186afaab8fa258"}

or invalid_user if the provided credentials are invalid or not allowed to use with this client.

Refresh Token [POST] /oauth/token/refresh

Request an new token if the old one has expired after an hour. Refresh tokens expire after 30 days.

Requires Basic Auth with your client_id as the username and the client_secret as the password.

Arguments

  • refresh_token: string, your refresh token from the last authorization

Output

{   "access_token": "12d6ba98f806343803c0c446ff0abcfda1302545",
    "expires_in": 3600,
    "token_type": "Bearer"}

Order

Get order properties [GET] /order/{id}

Get properties of an order. Requires editUsers for orders that don't belong to you.

Arguments

  • status: boolean, expand status to named codes, e.g. ["released", "delivered"] instead of just 288 (false)
  • owner: boolean, expand owner to a user object (false)
  • template: boolean, expand template to a template object (false)
  • released_by: boolean, expand released_by to a user object if present (false)

Output

"order": {
    "id": "51038",
    "title": "Inserat Bebbi Jazz",
    "preview": "https:\/\/server.net\/data\/4\/orders\/51038\/live.jpg"
    "status": "288",
    "owner": "1",
    "template": "33",
    "lang": "de", // ISO 639-1
    "quality": "300", // DPI
    "color_space": "cmyk",
    "format": "adsizer",
    "dimension": {
        "unit": "mm",
        "width": "138",
        "height": "210"
    },
    "created": "2013-03-12 16:13",
    "modified": "2013-03-12 16:14",
    "released": "2013-03-12 16:14",
    "delivered": "2013-03-12 16:14",
    "filesize": "12738560", // bytes
    "pages": "1",
    "pin": "37g6m",
    "remark": "This is my demo order",
    "released_by": 8 // controller, if any
    "reject_reason": "typo in headline" // if rejected
    "upload_name": "manually_layouted_file.eps" // if format is _manual_
    "translation_of": "345" // original order of translation if any

}

Status codes (bitmask)

Note: Status codes should be unset for the next step unless they are marked "sticky"

open             = 1;   // order may be edited, "sticky" until released
precontroll      = 2;   // optional workflow for manual orders before layouting
layout           = 4;   // layouting of a manual (e.g. not automatically renderable) order
control          = 8;   // waiting to be released by a controller
rejected         = 16;  // controller has rejected the order, "sticky" until released
released         = 32;  // controller has released the order, "sticky" forever
released         = 64;  // second controller (two-man rule, optional)
delivery_pending = 128; // order is ready to be downloaded (or "in print")
delivered        = 256; // order has been downloaded (or been mailed), "sticky" forever
charged          = 512; // order was charged (for per-PDF licensing)
exported         = 1024;// charge has been exported to an external system (e.g. SAP)

Set order properties [POST] /order/{id}

Set properties of an order. Requires editUserOrders (or editClients) for orders that don't belong to you. Changing the status manually requires editUsers in any case.

Arguments

  • title: string, title of the order
  • status: int, status code(s) (bitmask) of the order. This will overwrite all existing ones.
    Note: open should always be set until released - see the order state diagram.
  • set_flag: int, turn on a status flag without overwriting existing ones,
    e.g. if the status is 1 (open) and you set_flag=8 (control) the new status is 9 ([open, control])
  • unset_flag: int, turn off a status flag without overwriting existing ones
  • delivery: int, delivery method (1 = download, 2 = postage)

Output

{"success": "true"}

Delete order [DELETE] /order/{id}

Delete an order. Requires editUserOrders (or editClients) for orders that don't belong to you. Note that you can only delete an order that:

  • has the delivered flag set. This will set the deleted flag, meaning that a superadmin can still undelete it.
  • only has the open flag set. This will actually delete the order.

These restrictions apply so that orders don't suddenly disappear from workflows.

Output

{"success": "true"}

or forbidden if the above conditions are not met.

Get a preview PDF [GET] /order/preview/{id}

Render a preview PDF (72 DPI) of the order. Requires editUserOrders for those that don't belong to you.

Output

An inline application/pdf (or internal_server_error if the order could not be rendered)

Download order [GET] /order/download/{id}

Download an order (usually a PDF). Requires editUsers for orders that don't belong to you.

Output

The file ist streamed as an attachment with

  • Content-Type: application/octet-stream
  • Content-Transfer-Encoding: binary

or forbidden if the order isn't downloadable (due to status or ownership)

Order PIN

Note that a PIN basically makes an order public, so anyone who knows it can download the file.

Set PIN for an order [POST] /order/pin/{id}

Request a PIN to be created of this order. Requires editUsers for orders that don't belong to you.

Output

{"pin": "h58d2", "path": "http://papilio.my.net/pin/"}

Combine path + pin to get a "user readable" PIN link.

Download by PIN [GET] /order/pin/{pin}

Download an order (usually a PDF) that has been made public with a PIN code.

Output

Streamed the same way /order/download is, bad_request for a malformed PIN or not_found if there's no such PIN.

Delete PIN of an order [DELETE] /order/pin/{id}

Delete the PIN of an order. Requires editUsers for orders that don't belong to you.

Output

{"success": "true"}

Orders

Tip: You can use the arguments of /order here as well to get additional information.

List your orders [GET] /orders

List all completed orders of your user (i.e. not open and not deleted)

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

List all orders [GET] /orders/all/{year}

List the completed orders of all users, limited to a {year} (e.g. 2014, default is current year).
Requires editUsers.

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

List deleted orders [GET] /orders/deleted

List all deleted orders. Requires editClients.

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

Search orders [GET] /orders/search/{mode}

Search for orders with specific properties (with {mode} and (default) or or). Requires editUsers.

Arguments

  • title: string, (partial) title
  • status: int, orders having this status code - combinations are supported, e.g. released + delivered = 288
  • status_not: int, orders NOT having this status code
  • template: int, template id
  • owner: int, user id
  • lang: string, language code (ISO 639-1, e.g. 'de')
  • quality: int, DPI (most likely 300 or 150)
  • color_space: enum, one of: cmyk, rgb, gray
  • format: enum, one of: standard, manual, adsizer, std_size
  • created: date, ISO 8601, i.e. yyyy-mm-dd
  • modified: date, ISO 8601, i.e. yyyy-mm-dd
  • released: date, ISO 8601, i.e. yyyy-mm-dd
  • delivered: date, ISO 8601, i.e. yyyy-mm-dd

Note: date types support range search using _before and/or _after, e.g. created_after

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

Tasks

Layoutable orders [GET] /tasks/layoutable

List orders that need to be layouted by a human. Requires layoutOrders.

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

Use /order/download/{id} in order to get the automatically rendered PDF on which you should base the manual adaptation.

Printable orders [GET] /tasks/printable

List orders that need to be printed and sent. Requires printOrders.
Note that the orders will contain additional fields:

  • recipient: Name (and company, if any) of the recipient
  • address: address of the recipient
  • delivery_date: requested date of arrival, if any (ISO 8601)
  • copies: number of copies to print

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

Use /order/download/{id} to get the high end PDF suitable for printing.

Translatable orders [GET] /tasks/translatable

List orders that need to be translated. Requires translateOrders and you will only see orders where the current user can translate from the source language to the destination language.

Note: The owner field is used to denote who's working on a translation. If it is null the task has not been assigned yet and you can claim it. In order to claim or edit an order, send the user to /translation/edit/{id}

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

Completed orders [GET] /tasks/completed/{user}

List of orders that the user has completed. Requires editUsers for users other than the current one.

Output

{"count": 42, "orders": [{"id"; "56789", "title": "some order", ...}, {...}]}

Messages

Received messages [GET] /messages/received/{user}

List of messages that the user (or blank for the current user) has received.

Requires editUsers for users other than yours.

Output

{
    "count":42,
    "messages":[
       {
        "id": "2460",
        "title": "some subject",
        "body": "some <strong>text</strong>",
        "sent": true,
        "read": true,
        "timestamp": "2014-10-22 13:37",
        "sender": {"id":"186","first_name":"Joe", ... } // user object
       },
       {...}
    ]
}

Sent messages [GET] /messages/sent/{user}

List of messages that the user (or blank for the current user) has sent.

Requires editUsers for users other than yours.

Output

{
    "count":42,
    "messages":[
       {
        "id": "2461",
        "title": "some subject",
        "body": "some <strong>text</strong>",
        "sent": true,
        "read": false,
        "timestamp": "2014-10-22 17:37",
        "recipient": {"id":"186","first_name":"Joe", ... } // user object
       },
       {...}
    ]
}

Mark as read [POST] /messages/read/{id}

Marks the message with the {id} as read.

Output

{"success": "true"}

or forbidden if the message doesn't belong to you

Template

Template [GET] /template/{id}

Get information about a specific template.
Notes: To create a new order from a template simply send the user to /edit/create/{id}

Output

{"template": {
    "id": "34",
    "title": "Inserat A4 hoch",
    "preview": "https:\/\/server.net\/data\/4\/preview\/t_34_preview.jpg",
    "dimension": {
        "unit": "mm",
        "width": "210",
        "height": "297"
    },
    "parent": "16", // template group to which it belongs to
    "order": "2", // sort order within the template groups
    "pages": "1",
    "languages": ["de", "fr", "it"],
    "quality": ["150", "300"], // available output DPI
    "color_space": ["gray", "cmyk"],
    "delivery": "download", // download or postage
    "price": "0"
}}

or forbidden if you don't have the permission to see the template,
or not_found if the template doesn't exist or is not active.

Templates

Notes:

  • In order to create a new publication based on a template, send the user to /edit/create/{template id}.
  • order denotes the global sorting order of both templates and template groups.
  • preview is the base URL of the preview files, just add _{size}.jpg ({size}: micro, thumb or preview).
  • examples indicates that there are example publications (prefilled orders) available. In this case the template should be treated like a template group, the children of which may be retrieved using /templates/examples/{template id} and the user sent to /edit/clone_from/{order id} instead.

Children of a group [GET] /templates/children/{id}

Get a list of templates and template groups that are children of the template group with this {id}. Ommit the group parameter to get all children of the root node. Requires createOrders.

Output

{
    "count": 42,
    "children": [
       {
           "id": "8",
           "title": "Some group",
           "status": "2",
           "order": "0",
           "type": "group",
           "multiprev": "0",
           "examples": "0",
           "preview": "/data/4/preview/t_34"
       },
       {
           "id": "10",
           "title": "Some template",
           "status": "2",
           "order": "20",
           "type": "template",
           "multiprev": "0",
           "examples": "0",
           "preview": "/data/4/preview/t_34"
       },
       ...
    ],
    "parents": [ // in that order, for breadcrumbs et al.
        {"id": "1", "title": "top group"},
        {"id": "7", "title": "sub group"} // you are here
    ]
}

Nested view [GET] /templates/nested

Get a complete, nested array of all the template groups and their templates. Requires createOrders.

Output

{"nested": [
    [
        {
            "id": "8",
            "title": "Top level group",
            "status": "2",
            "order": "0",
            "type": "group",
            "multiprev": "0",
            "examples": "0",
            "preview": "/data/4/preview/g_8"
        },
        [
            [
                {
                    "id": "16",
                    "title": "Sub group",
                    "status": "2",
                    "order": "1",
                    "type": "group",
                    "multiprev": "0",
                    "examples": "0",
                    "preview": "/data/4/preview/g_16"
                },
                [...]
            ],
            {
                "id": "35",
                "title": "Template in top level group",
                "status": "2",
                "order": "19",
                "type": "template",
                "multiprev": "0",
                "examples": "0",
                "preview": "/data/4/preview/t_35"
            }
        ]
    ],
    [...],
    {
        "id": "36",
        "title": "Template on top level",
        "status": "2",
        "order": "20",
        "type": "template",
        "multiprev": "0",
        "examples": "0",
        "preview": "/data/4/preview/t_36"
    }
]}

Example publications [GET] /templates/examples/{id}/{lang}

Get all the example publications (orders) of the template with this {id}. If no {lang} parameter is present the current language will be used. Requires createOrders.

Output

{"count": 7, "orders": [{"id"; "8739", "title": "some order", ...}, {...}]}

Search [GET] /templates/search/{term}

Search available templates and example publications for a given {term}.
Note that this also searches in template groups and will return its direct children in case of a match.

Output

{
    "count": 7,
    "templates":[{"id": "49","title": "some template", ...}, {...}],
    "examples": [{"id"; "8739", "title": "some order", ...}, {...}]
}

Errors

Format:

  • Header with the appropriate HTTP status (4xx, 5xx)
  • Body:
    {   
    "error": "invalid_token",
    "error_description": "The access token provided has expired"
    }

General:

  • bad_request, the request is missing a required parameter or contains an invalid value (HTTP 400)
  • forbidden, the user does not have the required premissions to perform this action (HTTP 403)
  • not_found, the object you're looking for does not appear to exist (HTTP 404)
  • method_not_allowed, invalid http-method used, e.g. GET instead of POST (HTTP 405)
  • internal_server_error, something went wrong internally - check the server logs (HTTP 500)
  • not_implemented, the method that was called does not exist (HTTP 501)

Oauth:

  • invalid_client, this API client does not exist or may not be used in that scope (HTTP 400)
  • invalid_uri, the redirect URI used for interactive OAuth is invalid - check settings (HTTP 400)
  • invalid_grant, the username/password or authorization code provided is invalid (HTTP 400)
  • invalid_request, missing parameter or wrong format (HTTP 400)
  • invalid_token, the access_token is missing, invalid or has expired (HTTP 401)
  • invalid_user, the user account has expired, isn't active or may not be used with this client (HTTP 403)

Generic HTTP errors may also be thrown by the framework, where the error will start with httperror{number}