Cloud based e-seal

1. Configuration

Test environment

  1. Follow the Test environment API credentials guide to obtain the test API credentials.
  2. Contact support@eideasy.com to enable the e-seal service for your account.
  3. Use https://test.eideasy.com as the base_url in the API calls.
  4. Note that in test environment the e-seal will be created with an eID Easy test certificate and will not be legally binding.

Production environment

  1. Follow the API credentials guide to obtain the production API credentials.
  2. Contact support@eideasy.com to enable the e-seal service for your account.
  3. Use https://id.eideasy.com as the base_url in the API calls.

2. Usage

2.1 Prepare the file for signing

Send a server-side POST request to {{base_url}}/api/signatures/prepare-files-for-signing

The request body might look something like this:

{
  "files": [
    {
      "fileContent": "JVBERi0xLjMNCiXi48/TDQoNCjEgMCBvYmoNCjw8DQovVHlwZSAvQ2F0YWxvZw0KL091dGxpbmVzIDIgMCBSDQovUGFnZXMgMyAwIFINCj4+DQplbmRvYmoNCg0KMiAwIG9iag0KPDwNCi9UeXBlIC9PdXRsaW5lcw0KL0NvdW50IDANCj4+DQplbmRvYmoNCg0KMyAwIG9iag0KPDwNCi9UeXBlIC9QYWdlcw0KL0NvdW50IDINCi9LaWRzIFsgNCAwIFIgNiAwIFIgXSANCj4+DQplbmRvYmoNCg0KNCAwIG9iag0KPDwNCi9UeXBlIC9QYWdlDQovUGFyZW50IDMgMCBSDQovUmVzb3VyY2VzIDw8DQovRm9udCA8PA0KL0YxIDkgMCBSIA0KPj4NCi9Qcm9jU2V0IDggMCBSDQo+Pg0KL01lZGlhQm94IFswIDAgNjEyLjAwMDAgNzkyLjAwMDBdDQovQ29udGVudHMgNSAwIFINCj4+DQplbmRvYmoNCg0KNSAwIG9iag0KPDwgL0xlbmd0aCAxMDc0ID4+DQpzdHJlYW0NCjIgSg0KQlQNCjAgMCAwIHJnDQovRjEgMDAyNyBUZg0KNTcuMzc1MCA3MjIuMjgwMCBUZA0KKCBBIFNpbXBsZSBQREYgRmlsZSApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY4OC42MDgwIFRkDQooIFRoaXMgaXMgYSBzbWFsbCBkZW1vbnN0cmF0aW9uIC5wZGYgZmlsZSAtICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNjY0LjcwNDAgVGQNCigganVzdCBmb3IgdXNlIGluIHRoZSBWaXJ0dWFsIE1lY2hhbmljcyB0dXRvcmlhbHMuIE1vcmUgdGV4dC4gQW5kIG1vcmUgKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA2NTIuNzUyMCBUZA0KKCB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDYyOC44NDgwIFRkDQooIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNjE2Ljg5NjAgVGQNCiggdGV4dC4gQW5kIG1vcmUgdGV4dC4gQm9yaW5nLCB6enp6ei4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNjA0Ljk0NDAgVGQNCiggbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDU5Mi45OTIwIFRkDQooIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNTY5LjA4ODAgVGQNCiggQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA1NTcuMTM2MCBUZA0KKCB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBFdmVuIG1vcmUuIENvbnRpbnVlZCBvbiBwYWdlIDIgLi4uKSBUag0KRVQNCmVuZHN0cmVhbQ0KZW5kb2JqDQoNCjYgMCBvYmoNCjw8DQovVHlwZSAvUGFnZQ0KL1BhcmVudCAzIDAgUg0KL1Jlc291cmNlcyA8PA0KL0ZvbnQgPDwNCi9GMSA5IDAgUiANCj4+DQovUHJvY1NldCA4IDAgUg0KPj4NCi9NZWRpYUJveCBbMCAwIDYxMi4wMDAwIDc5Mi4wMDAwXQ0KL0NvbnRlbnRzIDcgMCBSDQo+Pg0KZW5kb2JqDQoNCjcgMCBvYmoNCjw8IC9MZW5ndGggNjc2ID4+DQpzdHJlYW0NCjIgSg0KQlQNCjAgMCAwIHJnDQovRjEgMDAyNyBUZg0KNTcuMzc1MCA3MjIuMjgwMCBUZA0KKCBTaW1wbGUgUERGIEZpbGUgMiApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY4OC42MDgwIFRkDQooIC4uLmNvbnRpbnVlZCBmcm9tIHBhZ2UgMS4gWWV0IG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA2NzYuNjU2MCBUZA0KKCBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY2NC43MDQwIFRkDQooIHRleHQuIE9oLCBob3cgYm9yaW5nIHR5cGluZyB0aGlzIHN0dWZmLiBCdXQgbm90IGFzIGJvcmluZyBhcyB3YXRjaGluZyApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY1Mi43NTIwIFRkDQooIHBhaW50IGRyeS4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA2NDAuODAwMCBUZA0KKCBCb3JpbmcuICBNb3JlLCBhIGxpdHRsZSBtb3JlIHRleHQuIFRoZSBlbmQsIGFuZCBqdXN0IGFzIHdlbGwuICkgVGoNCkVUDQplbmRzdHJlYW0NCmVuZG9iag0KDQo4IDAgb2JqDQpbL1BERiAvVGV4dF0NCmVuZG9iag0KDQo5IDAgb2JqDQo8PA0KL1R5cGUgL0ZvbnQNCi9TdWJ0eXBlIC9UeXBlMQ0KL05hbWUgL0YxDQovQmFzZUZvbnQgL0hlbHZldGljYQ0KL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcNCj4+DQplbmRvYmoNCg0KMTAgMCBvYmoNCjw8DQovQ3JlYXRvciAoUmF2ZSBcKGh0dHA6Ly93d3cubmV2cm9uYS5jb20vcmF2ZVwpKQ0KL1Byb2R1Y2VyIChOZXZyb25hIERlc2lnbnMpDQovQ3JlYXRpb25EYXRlIChEOjIwMDYwMzAxMDcyODI2KQ0KPj4NCmVuZG9iag0KDQp4cmVmDQowIDExDQowMDAwMDAwMDAwIDY1NTM1IGYNCjAwMDAwMDAwMTkgMDAwMDAgbg0KMDAwMDAwMDA5MyAwMDAwMCBuDQowMDAwMDAwMTQ3IDAwMDAwIG4NCjAwMDAwMDAyMjIgMDAwMDAgbg0KMDAwMDAwMDM5MCAwMDAwMCBuDQowMDAwMDAxNTIyIDAwMDAwIG4NCjAwMDAwMDE2OTAgMDAwMDAgbg0KMDAwMDAwMjQyMyAwMDAwMCBuDQowMDAwMDAyNDU2IDAwMDAwIG4NCjAwMDAwMDI1NzQgMDAwMDAgbg0KDQp0cmFpbGVyDQo8PA0KL1NpemUgMTENCi9Sb290IDEgMCBSDQovSW5mbyAxMCAwIFINCj4+DQoNCnN0YXJ0eHJlZg0KMjcxNA0KJSVFT0YNCg==",
      "fileName": "somefiletest.pdf",
      "mimeType": "application/pdf"
    }
  ],
  "client_id": "{{client_id}}",
  "secret": "{{secret}}",
  "container_type": "pdf",
  "show_visual": true,
  "noemails": true
}

fileContent - a base64 encoded PDF document.

noemails - Determines whether email notifications will be sent. Default value is true. For API clients created before 2024-08-28, the default value is false.

Note: Staging environment does not send out real emails.

Note: Don’t forget to set header Content-Type: application/json

If everything goes well the API responds with a doc_id like this:

{
  "status": "OK",
  "doc_id": "QLcJD03LUP0S7DUeTGNobQdsiJGfzqRnULrGE0iV",
  "expires_at": "2025-05-17T05:06:00Z"
}

Make a note of the doc_id value you receive (do not use the one in this example, it is expired) as you'll need it in the next steps.

NOTE expires_at in the response

Be aware that doc_id expires in 14 days. Make sure to prepare files for signing just before user goes to sign. Currently it is not possible extend the expiry time of the doc_id, but can call prepare endpoint again and get new doc_id.

2.2 Create an e-seal

Send a server-side POST request to {{base_url}}/api/signatures/e-seal/create

The request body might look something like this:

{
  "client_id":"{{client_id}}",
  "secret":"{{secret}}",
  "doc_id":"{{col_doc_id}}"
}
Request ParamDescription
client_idYour eID Easy API client_id
secretYour eID Easy API secret
doc_idThe same doc_id you got in step "1. Prepare the file for signing"

Hmac and timestamp are sent to the server in the request header.

HeaderDescription
X-TimestampUNIX timestamp in seconds, must not differ by more than 1 minute from the eID Easy server time
X-HMAC-SignatureSHA256 HMAC in base64 format created by appending the client_id, doc_id, timestamp and the request path (including leading slash /) and signing the result with your eID Easy eseal HMAC key. See the Postman docsopen in new window for a calculation example.

Here is the JavaScript reference for HMAC calculation:

let timestamp = Math.floor(Date.now() / 1000); // current UNIX timestamp in seconds
console.log(timestamp); // 1768990206
let requestURI = '/api/signatures/e-seal/create'; // always this path
let client_id = '9kKmNtYQ7LDOlHvasAE9oYQ0gKS4V4Bl'; // Get from id.eideasy.com after signing up.
let secret = 'XvsYIa0QBKySBOU2HXwPSPqtH6gaDcrv'; // Get from id.eideasy.com after signing up.
let doc_id = '3iSYuofit9jUSrEMrYP736Yd0dQNvBjo0B9x8hBv'; // Known after previous request: 2.1 Prepare the file for signing
let hmac_key = 'A9ZxKpM7cB2QyFJ0WvRHSaD4nU6EwT5'; // Get from id.eideasy.com once eseal method is configured for you

async function createHmac(key, message) {
    const enc = new TextEncoder();
    const cryptoKey = await crypto.subtle.importKey(
        'raw',
        enc.encode(key),
        { name: 'HMAC', hash: 'SHA-256' },
        false,
        ['sign']
    );

    const signature = await crypto.subtle.sign('HMAC', cryptoKey, enc.encode(message));
    return btoa(String.fromCharCode(...new Uint8Array(signature)));
}

let message = client_id + doc_id + timestamp + requestURI;
console.log(message); // message string is "9kKmNtYQ7LDOlHvasAE9oYQ0gKS4V4Bl3iSYuofit9jUSrEMrYP736Yd0dQNvBjo0B9x8hBv1768990109/api/signatures/e-seal/create"

let hmac = await createHmac(hmac_key, message);
console.log(hmac); // iJcn537VbFwPiMDGPBx2dyuRcpx7OAONiTlyqFHFUjU=

2.3 Fetch the e-sealed file

Make a POST request to {{base_url}}/api/signatures/download-signed-file with the following request body:

{
  "secret": "{{secret}}",
  "client_id": "{{client_id}}",
  "doc_id":"{{doc_id}}"
}

doc_id - the same doc_id you got in step "1. Prepare the file for signing"

NOTE

Just a friendly reminder that this request should be made server-side. You do not want to expose your secret in the front-end.

Example response:

{
  "status":"OK",
  "signed_file_contents":"UEsDBBQAAAgAAHI6z06KIflFHwAA",
  "filename":"signed_doc.pdf"
}

signed_file_contents - signed file in base64 encoding.

Last Updated: