User Guide
Developer Guide
Developer Guide Contents

Introduction

Quoting the user guide provides a nutshell description of the SignSpace service: 

SignSpace is a business platform for collaboration within and between organizations.

SignSpace allows organizations to share, refine, approve and manage any digital content in a secure way.

This developer guide explains how the features of the SignSpace service can be accessed via an application programming interface (API) and integrated to partners' solutions.

SignSpace architecture, simplified

As the SignSpace service itself is developed using the API, the interface offers a full set of services for partners. However, our aim is not to kickstart the development of multiple parallel implementations of the service, rather than to concentrate on key interfaces that existing and future backend systems can utilize:

The comprehensive SignSpace API documentation will be released in Q3 of 2019.

The identities of users and API clients are maintained using Gluu identity provider.

Technology overview

SignSpace is built with commonly used technology components and methods. The following listing describes the key elements of the developer-visible offering and provides a selection of documentation links. 

Component More information
REST - representational state transfer wikipedia
JSON - Javascript object notation wikipedia
OAuth - Open authorization framework wikipedia

The SignSpace API is platform-agnostic, it can be used in all platforms that support the HTTP-protocol.

Welcome!

This is an exciting moment for us as SignSpace developers, and we are thrilled to have you with us on this ride.

Please let us know if you spot any omissions, inconsistencies or any other issues in the service or the documentation. 

Authentication

Practicalities

Where is the SignSpace service? 

The partner-environment API is located at:

https://signspace.beta.tilaajavastuu.fi/api/v1

The production environment is located at:

https://app.signspace.com/api/v1/

For ease of use and brevity, the document hereafter uses a variable to refer to the used environment:

export URL=https://signspace.beta.tilaajavastuu.fi/api/v1

How do I get access to SignSpace service? 

Create an account for yourself in the service using the user interface.

How do I get an access token to use the SignSpace API?

The OAuth2 client credentials grant mechanism is used for authentication.

Please send your request for client credentials to partnerit@tilaajavastuu.fi. We will relay you the required values.

The identity provider for partner-environment is located at:

https://auth-signspace.beta.tilaajavastuu.fi/auth/token

Again, for convenience, fetch and store the token in an environment variable, after defining the values of CLIENT_ID- and SCOPES-variables according to the response from the customer service:

curl -X POST --user $CLIENT_ID -d "grant_type=client_credentials&scope=$SCOPES" https://auth-signspace.beta.tilaajavastuu.fi/auth/token

Afterwards, use the received access token in the requests as follows:

curl -X GET -H "Authorization: Bearer $TOKEN" "$URL/conversations.list"

How do I get a user-specific access token?

The client credentials-flow authorizes the client program to access SignSpace API, but does not authenticate any particular user.

For that functionality the OAuth2 authorization code grant would be used instead.

The operative words being "would be", full support for authorization code grant is a roadmap item currently, and we will update this page when it is available for use.

What e-mail address should I use on beta?

The beta-environment is aggressively whitelisted. Please use a mailinator.com address.

What kind of data should I use on beta?

Do not use your personal details. Do not upload any business-critical documents.

Resources

You can access the elements of the data model of the SignSpace service via resources in the API. A resource corresponds to a noun in a sentence, such as "message" or "user", that you operate on via the supported HTTP verbs. An singular uniquely identified individual entity that you operate on is called a resource instance (in this context the term essentially interchangeable with "object"). The "id"-property is used as to identify the individual resource instances and to refer to them.

The API does not provide an 1:1 mapping to the underlying data model. Hence it acts as an insulation layer between the implementation of the service and the integrations using it. This essentially enables large-scale changes within the SignSpace service without any effect on the interface and thus need for changes in the integrations. 

The key resources are as follows:

Resource Description
Message A message is sent by its author to one or more recipients. A messages is either an assigned task, a signing request for a single document, or just plain text. Any message may contain attached pdf documents or images, only pdf documents can be requested to be signed.
Space A space consists of a message and its replies. A space may contain messages of different types (plain messages, tasks and signing requests). A space has an access level, which determines to whom its messages may be sent.
File The maximum size of a file is 25 megabytes.
Group A group is a collection of users (and groups). A group is typically to establish a context, e.g. all communications related to a project or a customer case.
User A person who is able to login to SignSpace and utilize its features.
Organization A collection of users that use the same e-mail domain.

The API is REST-like, with rather definite remote procedure call overtones (method names explicitly define the functionality).

Each resource instance is identified with an unique string. And accessed as follows:

$URL/files.get?id=$FILE_ID

Parameters

As noted in resources, identifiers are always carried in the path of the request URL. 

Body parameters are used in POST, PUT and PATCH methods. Missing required parameters are flagged with a Bad Request-error. 

Query string parameters can be used to tweak the returned responses. 

List-parameters

When calling endpoints that return lists, the following query parameters can be used to control the results:

As an example, the following call provides up to twenty messages that start from the 21st message in the user's inbox: 

/users/me/messages?offset=20&limit=20

Examples

This page the first key flow as starting point for development.

The examples are done using curl, and the resulting JSON-documents have been prettyprinted using jq (results seriously abbreviated, since the data elements used in the responses contain a lot of properties). 

The examples assume availability of a base64-encoder (in order to upload a file).

The examples assume that environment variables are used to store tokens and identifiers as export TOKEN=the value as captured in Authentication-chapter.

Other environment variable are introduced as they become available in SignSpace responses.

Also, validating the results in the SignSpace service (e.g. new messages appear, state of a task changes) is an excellent way to familiarize yourself with the behaviour of the service.

The personal details used in the examples have been changed.

Get all messages addressed to me

This is the most basic scenario and essentially establishes that you've set up your client correctly.

curl -X GET -H "Authorization: Bearer $TOKEN" "$URL/conversations.list"

And the response, an array of messages starts like this:

[
  {
    "time": "2018-10-09T18:24:31",
    "creator_handle": "5b8a599e2854098c289e3d27",
    "owners_handles": [
      "5ba88bebd8d5a1008125d2f8"
    ],
    "owners_orgs_ids": [],
    "members_handles": [
      "5ba88bebd8d5a1008125d2f8"
    ],
    "available_to_handles": [
      "5ba88bebd8d5a1008125d2f8"
    ],
    "forwarded_message": null,
    "subject": "Welcome to SignSpace!",
    "type": "thread",
    "draft_text": null,
    "is_draft": false,
    "is_protected": false,
    "security_settings": {
      "verified_identities_required": false,
      "content_sharing_disabled": false,
      "message_preview_disabled": false
    },
    "created_at": "2018-10-09T18:24:31",
    "published_at": "2018-10-09T18:24:31",
    "last_message_combined_text": "Welcome to SignSpace! ...",
    "messages_count": 1,
    "orgs_settings": [
      {
        "org_id": null,
        "approvals_required": true
      }
    ],
    "_id": "5bbcf25f22100e0023891164",
    "requires_action_from_me": false,
    "starred_by_me": false,
    "mentions_me": false,
    "archived_by_me": false,
    "unread_by_me": false,
    "unread_by_me_and_mentions_me": false,
    "has_attachment": false,
    "title": "",
    "is_restricted": false,
    "members_text": "You"
  }
]
 

The most interesting property of the individual messages is the _id-field, which enables operating on that particular message.

export MESSAGE_ID=5bbcf25f22100e0023891164

Get a document signed by another user

This flow involves multiple steps and is the core of the integration of the signing implementation.

The flow creates a new space for the signing request message.

The flow is as shown in the following figure, in essentially two phases: first retrieve an access token, and then call the SignSpace API with that token.

First, define a handles for the integration robot that sends the request and for the signatory:

export BOT_HANDLE="robot handle supplied by SignSpace"

and create another handle for the recipient of the signature request:

export SIGNATORY_HANDLE="firstname.lastname@domain.com"

The second step is to upload the file to be signed into SignSpace. The file must be base64-encoded, the attached example encodes the document on the fly, but obviously the file can be attached in an already encoded form.

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" -d '{"acting_handle": "'$BOT_HANDLE'","base64": "'$(cat ./lemur.pdf|base64)'","name": "Puoliapinan kuva"}' "$URL/files.create"

The response shows the newly uploaded file.

          {
            "file": {
              "time": "2018-10-24T08:54:38",
              "creator_handle": "5ba88bebd8d5a1008125d2f8",
              "owners_handles": [
                "5ba88bebd8d5a1008125d2f8"
              ],
              "owners_orgs_ids": [],
              "available_to_handles": [
                "5ba88bebd8d5a1008125d2f8"
              ],
              "parent": null,
              "file": "5bd0334e7f5ef42259de91db",
              "name": "File #4",
              "hash": "86b8f001c4f6ac81afb2e5b57006591b5d1324d715d61ccecb46e94a807584ee",
              "content_type": "application/pdf",
              "created_at": "2018-10-24T08:54:38",
              "is_dir": false,
              "is_public": true,
              "is_hidden": false,
              "size": 71151,
              "_id": "5bd0334e7f5ef42259de91dd",
              "size_human": "69.5 kB"
            },
            "status": "ok"
          }

The file id will be used in the following steps, so capture it into a variable:

export FILE_ID=5bd0334e7f5ef42259de91dd

As expected, the properties of the file are available through the API as well:

curl -X GET -H "Authorization: Bearer $TOKEN" "$URL/files.get?id=$FILE_ID"

The signing request is created as follows, use as many handles for participants as is needed:

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" -d '{"files": ["'$FILE_ID'"],"title": "Signing request","description": "Please sign the attached document","acting_handle": "'$BOT_HANDLE'","participants": [{"handle": "'$SIGNATORY_HANDLE'","role": "signer"}]}' "$URL/tasks.signingRequest.create"

The response shows the status of the request.

          {
            "status": "ok",
            "task": {
              "time": "2018-10-24T08:59:04",
              "creator_handle": "5ba88bebd8d5a1008125d2f8",
              "owners_handles": [
                "5ba88bebd8d5a1008125d2f8"
              ],
              "owners_orgs_ids": [],
              "available_to_handles": [
                "5ba88bebd8d5a1008125d2f8"
              ],
              "unread_by_handles": [],
              "title": "Allekirjoituspyyntö",
              "description": "Please sign the attached document",
              "type": "signing_request",
              "completion_type": "all",
              "participants_list_is_flexible": false,
              "participants_list_order_respected": false,
              "status": "open",
              "due_date": null,
              "action_required_from_handles": [
                "5ba88bebd8d5a1008125d2f8"
              ],
              "participants_handles": [
                "5ba88bebd8d5a1008125d2f8"
              ],
              "mentions_handles": [],
              "participants": [
                {
                  "_id": "5bd0345722100e277f079dbc",
                  "handle": "5ba88bebd8d5a1008125d2f8",
                  "position": 0,
                  "role": "signer"
                }
              ],
              "files": [
                "5bd0334e7f5ef42259de91dd"
              ],
              "must_be_reviewed_before_signing": false,
              "signing_request": "5bd0345822100e277f079dbe",
              "_id": "5bd0345822100e277f079dc3",
              "mentions_me": false,
              "requires_action_from_me": true,
              "starred_by_me": false,
              "unread_by_me": false,
              "archived_by_me": false,
              "unread_by_me_and_mentions_me": false,
              "has_attachment": true,
              "access_request_type": "none",
              "description_plain": "Please sign the attached document"
            }
          }

Store the request id as follows:

export REQUEST_ID="5bbf42b7070201001d93dee6"

In order to specify the language used in communications with signatories and other recipients, use the following optional parameter in the request:

"recipient_language": "fi"

In order to specify the signature strength (i.e. whether a signatory is required to strongly authenticate), use the following optional parameter in the request:

"signature_strength": "strong"

Create a new space for the request:

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" -d '{"subject": "Request test","acting_handle": "'$BOT_HANDLE'","type": "thread","creator_handle": "'$SIGNATORY_HANDLE'","is_protected": false,"owners_handles": ["'$BOT_HANDLE'"],"members_handles": ["'$SIGNATORY_HANDLE'"],"orgs_settings": [{"org_id": null,"approvals_required": false}]}' "$URL/conversations.create"

The response shows the space.

        {
          "status": "ok",
          "conversation": {
            "time": "2018-10-24T09:02:39",
            "creator_handle": "5ba88bebd8d5a1008125d2f8",
            "owners_handles": [
              "5ba88bebd8d5a1008125d2f8"
            ],
            "owners_orgs_ids": [],
            "members_handles": [
              "5ba88bebd8d5a1008125d2f8"
            ],
            "available_to_handles": [
              "5ba88bebd8d5a1008125d2f8"
            ],
            "trans_context": {},
            "forwarded_message": null,
            "subject": "Request test",
            "type": "thread",
            "draft_text": "",
            "is_draft": true,
            "is_protected": false,
            "security_settings": {
              "verified_identities_required": false,
              "content_sharing_disabled": false,
              "message_preview_disabled": false
            },
            "created_at": "2018-10-24T09:02:39",
            "published_at": null,
            "messages_count": 0,
            "orgs_settings": [
              {
                "org_id": null,
                "approvals_required": false
              }
            ],
            "_id": "5bd0352f0702011e53cd0a1a",
            "requires_action_from_me": false,
            "starred_by_me": false,
            "mentions_me": false,
            "archived_by_me": false,
            "unread_by_me": false,
            "unread_by_me_and_mentions_me": false,
            "has_attachment": null,
            "is_restricted": false,
            "last_message_combined_text": "",
            "members_text": "You"
          }
        }

Store the conversation id as follows:

export THREAD_ID="5bbf42b7070201001d93dee6"

In order to avoid extra notificational e-mails being sent to signatories and other participants, you can use the following optional parameter in the request

send_notifications: false

Finally, create a message in the conversation that contains the siging request

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" -d '{"conversation": "'$THREAD_ID'","acting_handle": "'$BOT_HANDLE'","text": "This is a signature request sent by company X." ,"attachments": {"tasks": ["'$REQUEST_ID'"]}}' "$URL/conversations.postMessage"

The request is now visible and operable in the SignSpace user interface.

Best Practices, common questions & pitfalls

To be initiated and extended throughout the pilot period as questions and issues are resolved.

Return values & errors

Each endpoint returns headers (including an HTTP status code [see IANA for canonical definitions]) and a JSON document. Exceptions are possible, e.g. a successful DELETE-call returns only a status code, no document body at all). 

If the status code indicates an error, the response contains a JSON document that provides ahuman-readable explanation about the encountered fault.

In some cases the error is returned by the frameworks utilized by SignSpace, and the returned contents may differ.

Note that success in an operation is determined by the nature of the operation, and a "200" is not an universally applicable indication of success. 

Common error situations 

Status code

Explanation

400 Bad request - the request is missing a required parameter, a supplied parameter has a syntactically invalid value, or a combination of parameters is semantically invalid.
401 Unauthorized - the user named in the request is lacking privileges to complete an action (e.g. sign a documemnt).
402 Payment Required - the parameters of the contract have been exceeded and e.g. the number of licensed users needs to be increased. This is not in use yet.
403 Forbidden - the user named in the request is not present in SignSpace-service or is lacking privileges to complete an action (e.g. add an user to a space).
404 Not found - the requested resource instance is not present in the SignSpace-service.
408 Request Timeout - Internal timeout.
409 Conflict - Error updating a resource instance in the database.
429 Too Many Requests - the integration has sent too many requests recently. This is not in use yet.
500 Internal Server Error - attempting to serve the request has failed.

Especially with error code 500, please let us know what the circumstances of an observed fault are.

Throttling

SignSpace no throttling functionality implemented yet. 

Nonetheless, the integrations should utilize common sense and pace themselves when submitting multiple requests.

Even if there's no mechanism to prevent their creation and existence, we strongly suggest that integrations do not maintain unnecessary parallel connections. 

However, throttling is definitely a roadmap item, so HTTP status code 429 will be introduced in the future. 

Timeouts

As noted in the errors section, a timeout is announced with an explicit status code.

However, as a timeout may occur in network infrastructure outside of SignSpace, the response may vary depending on the origin (and thus not contain the any human-readable explanation on what happened). 

Downtime

Planned downtimes are announced in advance on the SignSpace blog.

Contracts

Every SignSpace customer operates within the parameters of a contract;.

As noted in the errors section, a an interaction by the customer that would exceed the contractual parameters returns an appropriate status code and the message-body contains a human-readable description about the exceeded parameter (if multiple parameters would be exceeded with a single interaction, at least one of them shall be identified in the message). 

Support

Please use the partnerit@tilaajavastuu.fi e-mail address to report discovered bugs and other issues.

Please be as precise as feasible when reporting errors: the request and response messages (or lack of a response) and an approximate timeframe of the discovered error are most helpful in resolving the issues in a timely and efficient manner.

Versioning

SignSpace API is developed with a "maximally downwards-compatible"-scheme. As JSON is a flexible way of transferring content, the following updates are possible without breaking compatibility: 

The API version is included in the request path, and upon a non-backwards-compatible update shall be changed from /v1 to a new version.

To upgrade an integration requires changing the version identifier in the request. 

In general the deprecation of previous API versions shall be announced well in advance of the actual change (the communications and procedures on this shall be developed during the pilot phase).

Roadmap

The SignSpace roadmap is essentially divided into two main categories, with the productization of the service as the watershed between the two. A lot of the roadmap elements concern user interface improvements, but additional interfaces and related features are included.

How do I propose features for the roadmap? 

Use the existing communication channels (preferably the spaces in SignSpace) to describe your needs and ideas for enhancements of SignSpace or its developer offering.