Direct flow

This is an integration pattern suited for senders with their own portals and web solutions, wishing to offer a seamless signing experience as a part of a process where the user is logged in through a senders web portal. The signature prosess will be perceived as an integrated part of the user flow. The user will be redirected to the senders website after the signing is completed. For more information of the flow, please see Signing with direct flow.

To ease the integration, we provide C# and Java libraries. If you are creating your own client, you will have to interact directly with the API. The message format of the API is XML, and relevant types can be found in direct.xsd.

Flow chart for signing in direct flow

Flow chart for signing in direct flow: The chart shows that a signer is sent to the signing portal from the sender’s website and completes the signing process. The sender gets the status, gets the signed document and confirms processing of the job. Solid lines show user flow and dashed lines shows requests to and responses from the API.

Having problems integrating?

Tip

Remember that if you are having problems creating a job in a direct signature flow, you can always get in touch with a developer on Github:

Get help for your C# integration here.

Step 1: Create signature job

ClientConfiguration clientConfiguration = null; //As initialized earlier
var directClient = new DirectClient(clientConfiguration);

var documentsToSign = new List<Document>
{
    new Document(
        "Document title",
        FileType.Pdf,
        @"C:\Path\ToDocument\File.pdf")
};

var exitUrls = new ExitUrls(
    new Uri("http://redirectUrl.no/onCompletion"),
    new Uri("http://redirectUrl.no/onCancellation"),
    new Uri("http://redirectUrl.no/onError")
);

var signers = new List<Signer>
{
    new Signer(new PersonalIdentificationNumber("12345678910")),
    new Signer(new PersonalIdentificationNumber("10987654321"))
};

var job = new Job("Job title", documentsToSign, signers, "SendersReferenceToSignatureJob", exitUrls);

var directJobResponse = await directClient.Create(job);

Note

You can specify a signature type and required authentication level. If signature type or required authentication level is omitted, default values will be set as specified by Signature type and Security level.

List<Document> documentsToSign = null; //As initialized earlier
ExitUrls exitUrls = null; //As initialized earlier
var signers = new List<Signer>
{
    new Signer(new PersonalIdentificationNumber("12345678910"))
    {
        SignatureType = SignatureType.AdvancedSignature
    }
};

var job = new Job(documentsToSign, signers, "SendersReferenceToSignatureJob", exitUrls)
{
    AuthenticationLevel = AuthenticationLevel.Four
};

Identifying the signer

When using direct flow you can identify the signer in two different ways:

  • using their personal identification number (national ID). This is required if you prefer to include signers’ national ID in the signatures and visible in the resulting signed document.

  • using your own custom identifier, which is unique per signature job. E.g. a customer number or any string allowing your integration to distinguish each signer from one another within a signature job.

Direct.Signer nationalIdSigner =
        new Direct.Signer(new PersonalIdentificationNumber("12345678910"));

Direct.Signer customIdSigner =
        new Direct.Signer(new CustomIdentifier("customer-1234"));

On behalf of

A sender may specify if the signer is signing on behalf of themself or by virtue of a role.

The sender should always set this attribute according to the nature of the signature; if the person signs for a personal matter (SELF, default if not specified), or signing on behalf of anything not personal (OTHER), e.g. a contract for an organization or any other non-personal document.

Specifying on behalf of OTHER will slightly alter the behavior of some aspects of the signing flow, including but may not be limited to:

  • It prevents automatic forwarding the document to the signer’s personal digital mailbox (Digipost for private sector organizations, and the signer’s chosen provider of digital mailbox for public sector).

  • The signer will be informed during signing that the signature will be on behalf of “another entity”, i.e. not on behalf of themselves.

Direct.Signer signer =
        new Direct.Signer(new PersonalIdentificationNumber("12345678910"))
        {
            OnBehalfOf = OnBehalfOf.Other
        };

Other settings

Identifier in the signed document

//This functionality exists in C#, but the example has not been generated yet.
//For now, please refer to the HTTP tab, as the concepts described there have
//equivalent representations in the .NET client library.

Status retrieval method

//This functionality exists in C#, but the example has not been generated yet.
//For now, please refer to the HTTP tab, as the concepts described there have
//equivalent representations in the .NET client library.

Response

//This functionality exists in C#, but the example has not been generated yet.
//For now, please refer to the HTTP tab, as the concepts described there have
//equivalent representations in the .NET client library.

Step 2: Signing the document

This whole step is carried out in the signing portal. You forward the user to the portal using the URL you receive in response to the creation of the job. This URL contains a one-time token generated by the signature service, and it is this token that allows the user to read the documents and complete the signing. If the user aborts the signing, a new redirect URL must be requested in order to access the signature job again. Please see Request new redirect URL to request a new redirect URL.

Important

Security in connection with the one-time token: To handle the security of this request, the token will only work once. The user will receive a cookie from the signature service when accessing the URL, so that any refresh does not stop the flow. This URL cannot be reused at a later time. The reason we only allow it to be used only once is that URLs can appear in logs, and it will therefore not be safe to reuse.

The user completes the signing and is then returned to the sender’s portal via the URL specified by completion url. At the end of this URL, a query parameter (status_query_token) will be added, which you will use later when you ask for the signature job status. If the signer interrupts the signing, or an error occurs, the signer will be sent to the rejection-url or the error-url respectively.

Step 3: Get status

Status by token

The signing process is a synchrounous operation in the direct use case. There is no need to poll for changes to a signature job, as the status is well known to the sender of the job. As soon as the signer completes, rejects or an error occurs, the user is redirected to the respective URLs set in ExitUrls. A status_query_token parameter has been added to the url, use this when requesting a status change.

ClientConfiguration clientConfiguration = null; //As initialized earlier
var directClient = new DirectClient(clientConfiguration);
JobResponse jobResponse = null; //As initialized when creating signature job
var statusQueryToken = "0A3BQ54C...";

var jobStatusResponse =
    await directClient.GetStatus(jobResponse.StatusUrl.Status(statusQueryToken));

var jobStatus = jobStatusResponse.Status;

Status by polling

If you, for any reason, are unable to retrieve status by using the status query token specified above, you may poll the service for any changes done to your organization’s jobs. If the queue is empty, additional polling will give an exception.

Note

For the job to be available in the polling queue, make sure to specify the job’s StatusRetrievalMethod as illustrated below.

ClientConfiguration clientConfiguration = null; // As initialized earlier
var directClient = new DirectClient(clientConfiguration);

// Repeat the polling until signer signs the document, but ensure to do this at a
// reasonable interval. If you are processing the result a few times a day in your
// system, only poll a few times a day.
var change = await directClient.GetStatusChange();

switch (change.Status)
{
    case JobStatus.NoChanges:
        // Queue is empty. Additional polling will result in blocking for a defined period.
        break;
    case JobStatus.CompletedSuccessfully:
        // Get PAdES
        break;
    case JobStatus.Failed:
        break;
    case JobStatus.InProgress:
        break;
    default:
        throw new ArgumentOutOfRangeException();
}

// Confirm status change to avoid receiving it again
await directClient.Confirm(change.References.Confirmation);

var pollingWillResultInBlock = change.NextPermittedPollTime > DateTime.Now;
if (pollingWillResultInBlock)
{
    //Wait until next permitted poll time has passed before polling again.
}

Tip

As illustrated above, you should always query the statusChange to find out when you are allowed to poll for statuses next time.

Step 4: Get signed documents

ClientConfiguration clientConfiguration = null; //As initialized earlier
var directClient = new DirectClient(clientConfiguration);
JobStatusResponse jobStatusResponse = null; // Result of requesting job status

if (jobStatusResponse.Status == JobStatus.CompletedSuccessfully)
{
    var padesByteStream = await directClient.GetPades(jobStatusResponse.References.Pades);
}

Step 5: Confirm finished processing

//This functionality exists in C#, but the example has not been generated yet.

Specifying queues

An important and necessary feature for organizations using more than one application to create signature jobs through the API. It enables an application to retrieve status changes independent of other applications.

The feature specifies the queue that jobs and status changes for a signature job will occur in. It is used for signature jobs where StatusRetrievalMethod == POLLING. If your organization is using more than one application/integration to access our API, we strongly recommend using a separate queue for each one. This is to ensure that one does not retrieve the others’ receipts. This may result in missing status changes for jobs in one of the applications, which in turn will result in a poor user experience. Only use the default queue, eg. not specifying a queue, when only one of your applications access our API.

To specify a queue, set pollingQueue through when constructing a Sender. Please note that the same sender must be specified when polling to retrieve status changes. The Sender can be set globally in ClientConfiguration or on every job.

ClientConfiguration clientConfiguration = null; // As initialized earlier
var directClient = new DirectClient(clientConfiguration);

String organizationNumber = "123456789";
var sender = new Sender(organizationNumber, new PollingQueue("CustomPollingQueue"));

List<Document> documentsToSign = null; // As initialized earlier
ExitUrls exitUrls = null; // As initialized earlier

var signer = new PersonalIdentificationNumber("00000000000");

var job = new Job(
    documentsToSign,
    new List<Signer> { new Signer(signer) },
    "SendersReferenceToSignatureJob",
    exitUrls,
    sender,
    StatusRetrievalMethod.Polling
);

await directClient.Create(job);

var changedJob = await directClient.GetStatusChange(sender);

Delete documents

After receiving a status change, the documents can be deleted as follows:

//This functionality exists in C#, but the example has not been generated yet.
//For now, please refer to the Java tab, as the API and concepts described there have
//equivalent representations in the .NET client library.

Request new redirect URL

For security reasons, the redirect URL for a signer can only be used once. If the signing process is to be initiated again, a new redirect URL must be requested.

If the JobResponse is kept in memory from job creation until a new URL is requested, it can be done like this:

ClientConfiguration clientConfiguration = null; //As initialized earlier
Job job = null; //As created earlier
var directClient = new DirectClient(clientConfiguration);
var directJobResponse = await directClient.Create(job);

var signerFromResponse = directJobResponse
    .Signers
    .First(s => s
        .Identifier
        .IsSameAs(new PersonalIdentificationNumber("12345678910"))
    );

var signerWithUpdatedRedirectUrl = await directClient
    .RequestNewRedirectUrl(signerFromResponse);
var newRedirectUrl = signerWithUpdatedRedirectUrl.RedirectUrl;

Otherwise, do like this:

ClientConfiguration clientConfiguration = null; //As initialized earlier
Job job = null; //As created earlier
var directClient = new DirectClient(clientConfiguration);
var directJobResponse = await directClient.Create(job);

// Step 1:
foreach (var signer in directJobResponse.Signers)
{
    //Persist signer URL in sender system
    var signerResponseSignerUrl = signer.SignerUrl;
}

// ... some time later ...

// Step 2: Request new redirect URL for signer
Uri persistedSignerUrl = null; //Persisted URL from step 1.
var signerWithUpdatedRedirectUrl = await directClient
    .RequestNewRedirectUrl(
        NewRedirectUrlRequest
            .FromSignerUrl(persistedSignerUrl)
    );
var newRedirectUrl = signerWithUpdatedRedirectUrl.RedirectUrl;