Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Při integraci Signi - Microsoft produkty provozované On-Premise např. MS Sharepoint, MS Navision, starší MS Dynamics apod. je třeba volat napřímo Signi API.

API využijete nejpravděpodobněji v následujícícm režimu:

...

posílat soubory, PDF/DOCX

...

pro autentizaci použijete jeden servisní účet

...

půjde o distanční  podepsaní - podepisujícím se odesílají maily s odkazem

...

Table of Contents

Přímé volání Signi API

Výhodou přímého volání Signi API je flexibilita, novinky v Signi API jsou ihned dostupné pro využití. Je možné paralelně zpracovávat více dokumentů na jednou. Je možné ho použít jak z cloudových tenantů tak on premise instalací. Pro volání z Microsoft Power Automate se využijí operace HTTP a Webhook.

Před voláním API je třeba si vygenerovat v Signi aplikaci API Klíč pro pracovní prostor / worspaceworkspace, do kterého budete dokumenty k podpisu předávat viz Generování API klíče

Detailní volání použití jednotlivých end pointů: 

...

https://ismlouva3rdpartyservice1.docs.apiary.io/#reference/vytvoreni-smlouvy-2.0/z-dokumentu/bez-priloh

...

https://ismlouva3rdpartyservice1.docs.apiary.io/#reference/smlouva/smlouva/detail-smlouvy

...

https://ismlouva3rdpartyservice1.docs.apiary.io/#reference/smlouva/smlouva/stahnout-pdf

...

Volat pak můžete kterýkoliv z End Pointů Signi API.

Následující příklady ukazují volání SIgni API ze tří různých technologií Microsoftu

  1. Microsoft Power Automate,

  2. kód v .NET,

  3. kód ve Visual Basic for Application - VBA.

Příklad volání Signi API z MS Power Automate

MS Power Automate je integrační služba, která je součástí technologické platformy Microsoft Power Platform. Příklad volání Signi API z MS Power Automate se skládá ze tří flow:

  • Signi Demo - Podepsání dokumentu ze vzoru- vyvolá podpis dokumentu vytvořeného ze vzoru “Testovací smlouva k podpisu” uloženém v Signi a zpřístupněném v testovacím workspace Demo API,

  • Signi Demo - Podepsání PDF souboru - předá k podpisu PDF soubor,

  • Signi Demo - Webhook - vytvoří v prostředí MS Power Automate webhook pro přijetí výsledků podepsání dokumentu, odkazuje se na ně první flow.

...

Pro vyzkoušení je na produkčním prostředí Signi workspace "Demo API" a má API klíč = “71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f” , při svých voláních zaměňte tento API klíč za API klíč svého workspace, který získáte po objednání služby "Integrace API" a Generování API klíče.

Jednotlivé flow z příkladů níže si můžete stáhnout zde.

Vytvoření flow pro podepsání dokumentu ze vzoru

Vytvořte nové flow přes  MS Power Automate > Create (v levém panelu) > Instant cloud flow (pro ruční spuštění ) > Manually create flow.

...

Vložit do flow operaci přes Next step > HTTP  (vybrat tento typ operace).

...

Parametry operace HTTP pro vyvolání vzoru obdobně jako v Signi HELP > API Integrace > Příklad 3 - Předání podkladů pro vzor

  • Požadavek typu POST

  • Adresa EndPointu je https://api.signi.com/api/v1/contract/?type=template

  • V HEAD pro autentizaci API klíč v poli s názvem x-api-key 

  • Pro účely dema je na produkčním prostředí Signi https://api.signi.com je workspace "Demo API" a má API klíč = “71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f” , při svých voláních zaměňte tento API klíč za API klíč svého workspace, který získáte po objednání služby "Integrace API" a Generování API klíče.

  • V body je potřebný JSON se všemi parametry pro vytvoření dokumentu, url webhooku odpovídá webhooku vytvořeném dále, lze přidat po otestování tohoto flow.

  • E-mail demo+counterparty@signi.com si můžete změnit na svůj.

Code Block
{
  "settings": {
    "signing_order": "proposers_before_counterparties",
    "autosign_proposers": "V Praze"
  },
  "people": [
    {
      "is_proposer": true,
      "email": "demo@signi.com",
      "contract_role": "sign"
    },
    {
      "is_proposer": false,
      "party_order": 1,
      "email": "demo+counterparty@signi.com",
      "contract_role": "sign",
      "person_type": "nature",
      "first_name": "John",
      "last_name": "Doe2"
    }
  ],
  "webhooks": [
    {
      "state": "completed",
      "url": "https://prod-174.westeurope.logic.azure.com:443/workflows/4255c88e046844319c27fdf997ee7eed/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=lJhO1j7pXPUUzQ6apYhS1TJmI6HoEyN4NS_cBMI-6rw"
    }
  ],
  "template": {
    "id": "7v1",
    "parameters": [
      {
        "id": "112",
        "value": "Hnutí za digitalní revoluci"
      },
      {
        "id": "131",
        "value": "Chci"
      },
      {
        "id": "411",
        "value": "V šíření zpráv"
      },
      {
        "id": "421",
        "value": "27.5.2021"
      },
      {
        "id": "431",
        "value": "v Praze"
      }
    ]
  }
}

...

Uložit flow přes Save.

...

Pro testování zvolit  Test a Run Flow .

...

Výsledek odpovědi Signi API na požadavek přístupný v MS Power Automate: 

...

Vytvoření flow pro podepsání PDF souboru

Vytvořte nové flow přes  MS Power Automate > Create (v levém panelu) > Instant cloud flow (pro ruční spuštění ) > Manually create flow.

Ve flow pro podepsání PDF souboru je třeba nejprve do flow získat soubor k podpisu, zde používame operátor Get file content .

...

Následně vyvoláme opet operátor HTTP, Parametry operace HTTP pro vyvolání vzoru obdobně jako v Signi HELP > API Integrace > Příklad 2 - Předání PDF dokumentu k podpisu

  • Požadavek typu POST

  • Adresa EndPointu je https://api.signi.com/api/v1/contract/?type=doc

  • V HEAD pro autentizaci API klíč v poli s názvem x-api-key 

  • Pro účely dema je na produkčním prostředí Signi https://api.signi.com je workspace "Demo API" a má API klíč = “71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f” , při svých voláních zaměňte tento API klíč za API klíč svého workspace, který získáte po objednání služby "Integrace API" a Generování API klíče.

  • V body je potřebný JSON se všemi parametry pro vytvoření dokumentu, url webhooku odpovídá webhooku vytvořeném dále, lze přidat po otestování tohoto flow.

  • E-mail demo+counterparty@signi.com si můžete změnit na svůj.

Code Block
{
  "$content-type": "multipart/form-data",
  "$multipart": [
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"metadata\"; filename=\"data.json\""
      },
      "body": {
        "name": "test",
        "number": "123456",
        "locale": "cs",
        "webhooks": [
          {
            "state": "completed",
            "url": "https://prod-174.westeurope.logic.azure.com:443/workflows/4255c88e046844319c27fdf997ee7eed/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=lJhO1j7pXPUUzQ6apYhS1TJmI6HoEyN4NS_cBMI-6rw"
          }
        ],
        "settings": {
          "signing_order": "one_at_a_time",
          "missing_positions": "append_to_the_end"
        },
        "people": [
          {
            "is_proposer": true,
            "party_order": 1,
            "email": "demo@signi.com",
            "contract_role": "sign"
          },
          {
            "is_proposer": false,
            "party_order": 2,
            "email": "demo+counterparty@signi.com",
            "phone": "723653670",
            "first_name": "John",
            "last_name": "Doe#1",
            "contract_role": "sign",
            "person_type": "nature"
          }
        ],
        "file": "file_key"
      }
    },
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"file_key\"; filename=\"test.pdf\""
      },
      "body": {
        "$content-type": "application/pdf",
        "$content": "@{body('Get_file_content')?['body']}"
      }
    }
  ]
}

v JSONu se potřebujeme odkázat na soubor získaný v předchozím kroku , k tomu použijeme dynamický obsah File Content.

...

….

...

Další průběh testování je shodný s flow pro vytvoření dokumentu ze vzoru.

Vytvoření flow pro webhook pro přijetí výsledku podepsání

Vytvořte nové flow přes MS Power Automate v levém panelu > Create > Instant cloud flow pro ruční spuštění  > Manually create flow

Vložte do flow operaci přes Next step > When an HTTP request is received  (vybrat tento typ operace).

...

Jako šablonu dat, které webhook dostane, je třeba vložit příklad JSONu viz příklad na https://signi.docs.apiary.io/#reference/

...

Pro úvodní ověření napojení na SIgni je dobré volat jednoduchou funkci vracející pouze parametry volaného pracovního prostoru/workspace v Signi:

https://ismlouva3rdpartyservice1.docs.apiary.io/#reference/autorizace

Inspirace k volání viz také Příklady volání API.

Mnoho úspěchů. V případě potíží se obracejte na help@signi.com . webhooky/ze-vzoru-s-prilohami tj, 

Code Block
{
    "type": "object",
    "properties": {
        "contract_id": {
            "type": "integer"
        },
        "state": {
            "type": "string"
        },
        "file": {
            "type": "string"
        },
        "attachments": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "contract_id": {
                        "type": "integer"
                    },
                    "state": {
                        "type": "string"
                    },
                    "file": {
                        "type": "string"
                    }
                },
                "required": [
                    "contract_id",
                    "state",
                    "file"
                ]
            }
        }
    }
}


...

Jako druhá je do flow vložená operace Parse JSON , kde se řekne, že se bude zpracovávat kód z Body a jako příklad JSON schématu se bere to, co dostává webhook ze Signi viz výše. Zároveň se ikonou Copy získá URL webhooku , který se následně vyplní jako parametr volání Signi v prvním a druhém flow příkladu.

...

Výsledek volání webhooku lze zjistit v MS Power Automate v historii flow Signi Demo - webhook,  na stránce detailu dole zobrazit v  historii poslední volání , kde jsou vidět výsledky volání.

...

Table of Contents
minLevel1
maxLevel7

Příklad kódu v .NET

.NET je prostředí, framework Microsoftu pro vývoj aplikací.

  • Na produkčním prostředí Signi je workspace "Demo API" a má API klíč = “71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f” , při svých voláních zaměňte tento API klíč za API klíč svého workspace, který získáte po objednání služby "Integrace API" a Generování API klíče.

  • Pro Pro zasílání HTTP požavavků se využívá knihovna RestSharp, https://restsharp.dev/

  • Níže jsou volání různých enpointů Signi API.

Code Block
using Art.Gaia;
using Art.IDocuments.Models;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

using Newtonsoft.Json;

using RestSharp;

using System;
using System.Collections.Generic;
using System.Text;

using BE = Art.BusinessEntity;

namespace Art.IDocuments.Logic
{
    public class AnnotationLogic
    {
        public Base _base;
        public AnnotationLogic(Base myBase)
        {
            _base = myBase;
        }


// Upload From File 

        public string Upload(EntityReference dokumentReference)
        {
            try
            {
                BE.art_idokument dokument = _base.Service.Retrieve(BE.art_idokument.EntityLogicalName, dokumentReference.Id,
                    new ColumnSet(BE.art_idokument.Attr.art_customerid.LogicalName, BE.art_idokument.Attr.art_name.LogicalName, BE.art_idokument.Attr.art_sablonaid.LogicalName)).ToEntity<BE.art_idokument>();
                BE.art_idokumentsablona sablona = _base.Service.Retrieve(dokument.art_sablonaid.LogicalName, dokument.art_sablonaid.Id,
                    new ColumnSet(BE.art_idokumentsablona.Attr.art_emailautor.LogicalName, BE.art_idokumentsablona.Attr.art_mistopodpisu.LogicalName, BE.art_idokumentsablona.Attr.art_spolecnost.LogicalName, BE.art_idokumentsablona.Attr.art_stranapospisu.LogicalName)).ToEntity<BE.art_idokumentsablona>();
                BE.contact contact = _base.Service.Retrieve(BE.contact.EntityLogicalName, dokument.art_customerid.Id,
                    new ColumnSet(BE.contact.Attr.firstname.LogicalName, BE.contact.Attr.lastname.LogicalName, BE.contact.Attr.emailaddress1.LogicalName, BE.contact.Attr.mobilephone.LogicalName, BE.contact.Attr.address1_line1.LogicalName, BE.contact.Attr.description.LogicalName)).ToEntity<BE.contact>();
                QueryExpression queryAnno = new QueryExpression
                {
                    EntityName = BE.annotation.EntityLogicalName,
                    NoLock = true,
                    TopCount = 1,
                    ColumnSet = new ColumnSet(BE.annotation.Attr.filename.LogicalName, BE.annotation.Attr.mimetype.LogicalName, BE.annotation.Attr.documentbody.LogicalName),
                    Criteria = new FilterExpression(LogicalOperator.And)
                    {
                        Conditions =
                        {
                            new ConditionExpression(BE.annotation.Attr.objectid.LogicalName,ConditionOperator.Equal,dokument.Id)
                        }
                    }
                };
                List<BE.annotation> annotations = _base.Service.RetrieveMultiple<BE.annotation>(queryAnno);
                if (annotations.Count == 0) return "Žádné přílohy";
                BE.annotation anno = annotations[0];
                byte[] bytes = Convert.FromBase64String(anno.documentbody);
                if (anno.filename.Contains("html"))
                {
                    byte[] data = Convert.FromBase64String(anno.documentbody);
                    string decodedString = Encoding.UTF8.GetString(data);
                    string newBodyText = decodedString.Replace("{data_z_dynamics1}", contact.description).Replace("{data_z_dynamics2}", contact.address1_line1);
                    bytes = Encoding.UTF8.GetBytes(newBodyText);
                }

                // Dokument stažený z Dynamics 365 DB
                var client = new RestClient("https://api.signi.com/api/v1/contract/sign/provided")
                {
                    Timeout = -1
                };
                //client.UseNewtonsoftJson();
                var request = new RestRequest(Method.POST);
                //request.UseNewtonsoftJson();
                request.AddHeader("x-api-key", "71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f");
                request.AddHeader("Content-Type", "multipart/form-data");
                request.AddParameter("email_author", sablona.art_emailautor);
                request.AddParameter("email_signer", contact.emailaddress1);
                request.AddParameter("phone_signer", contact.mobilephone);
                request.AddParameter("firstname_signer", contact.firstname);
                request.AddParameter("lastname_signer", contact.lastname);
                request.AddParameter("proposer_sign", "true");
                request.AddParameter("negotiator_sign", "true");
                request.AddParameter("contract_name", dokument.art_name);
                request.AddParameter("contract_number", dokument.art_cislodokumentu);
                request.AddParameter("sign_date", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
                request.AddParameter("sign_place", sablona.art_mistopodpisu);
                request.AddParameter("last_document", "true");
                request.AddParameter("person_type", "legal");
                //request.AddParameter("date_of_birth", "1970-04-16");
                //request.AddParameter("street", "5. května 67");
                //request.AddParameter("city", "Libčice nad Vltavou");
                //request.AddParameter("zip_code", "25266");
                request.AddParameter("company_name", sablona.art_spolecnost);
                //request.AddParameter("ic", "28524322");
                //request.AddParameter("dic", "CZ28524322");
                //request.AddParameter("sign_proposer", "{\"position\": {\"x\": 15,\"y\": 84,\"page\": 0}}");
                //request.AddParameter("sign_negotiator", "{\"position\": {\"x\": 78,\"y\": 84,\"page\": 0}}");
                request.AddParameter("sign_proposer[position][x]", 8);
                request.AddParameter("sign_proposer[position][y]", 84);
                request.AddParameter("sign_proposer[position][page]", sablona.art_stranapospisu - 1);
                request.AddParameter("sign_negotiator[position][x]", 64);
                request.AddParameter("sign_negotiator[position][y]", 84);
                request.AddParameter("sign_negotiator[position][page]", sablona.art_stranapospisu - 1);
                request.AddParameter("webhooks[0][state]", "signed");
                request.AddParameter("webhooks[0][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookSigned");
                request.AddParameter("webhooks[1][state]", "rejected");
                request.AddParameter("webhooks[1][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookRejected");
                request.AddParameter("webhooks[2][state]", "expired");
                request.AddParameter("webhooks[2][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookExpired");
                request.AlwaysMultipartFormData = true;
                request.AddFileBytes("file", bytes, anno.filename, anno.mimetype);
                IRestResponse response = client.Execute(request);
                if (response.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    BE.art_idokument iDokumentUpdate = new BE.art_idokument
                    {
                        Id = dokument.Id,
                        art_id = JsonConvert.DeserializeObject<UploadDokumentResponseClass>(response.Content).ContractId,
                        art_idokumentid = dokument.Id,
                        statuscode = new OptionSetValue(BE.art_idokument.Attr.statuscode.Options.A_Odeslany_k_podpisu)
                    };
                    _base.Trace = true;
                    _base.Debug(iDokumentUpdate, "iDokumentUpdate");


                    iDokumentUpdate.Update(_base.Service);
                    return response.Content;
                }
                return response.Content;
            }
            catch (Exception e)
            {
                _base.Error(e, "Art.IDocuments.Logic.AnnotationLogic.Upload");
                return e.Message;
            }
        }

// Upload From Template


        public string UploadTemplate(EntityReference dokumentReference)
        {
            try
            {
                BE.art_idokument dokument = _base.Service.Retrieve(BE.art_idokument.EntityLogicalName, dokumentReference.Id,
                    new ColumnSet(BE.art_idokument.Attr.art_customerid.LogicalName, BE.art_idokument.Attr.art_name.LogicalName, BE.art_idokument.Attr.art_sablonaid.LogicalName)).ToEntity<BE.art_idokument>();
                BE.art_idokumentsablona sablona = _base.Service.Retrieve(dokument.art_sablonaid.LogicalName, dokument.art_sablonaid.Id,
                    new ColumnSet(BE.art_idokumentsablona.Attr.art_emailautor.LogicalName, BE.art_idokumentsablona.Attr.art_mistopodpisu.LogicalName, BE.art_idokumentsablona.Attr.art_spolecnost.LogicalName, BE.art_idokumentsablona.Attr.art_stranapospisu.LogicalName)).ToEntity<BE.art_idokumentsablona>();
                BE.contact contact = _base.Service.Retrieve(BE.contact.EntityLogicalName, dokument.art_customerid.Id,
                    new ColumnSet(BE.contact.Attr.firstname.LogicalName, BE.contact.Attr.lastname.LogicalName, BE.contact.Attr.emailaddress1.LogicalName, BE.contact.Attr.mobilephone.LogicalName, BE.contact.Attr.address1_line1.LogicalName, BE.contact.Attr.description.LogicalName)).ToEntity<BE.contact>();
                QueryExpression queryAnno = new QueryExpression
                {
                    EntityName = BE.annotation.EntityLogicalName,
                    NoLock = true,
                    TopCount = 1,
                    ColumnSet = new ColumnSet(BE.annotation.Attr.filename.LogicalName, BE.annotation.Attr.mimetype.LogicalName, BE.annotation.Attr.documentbody.LogicalName),
                    Criteria = new FilterExpression(LogicalOperator.And)
                    {
                        Conditions =
                        {
                            new ConditionExpression(BE.annotation.Attr.objectid.LogicalName,ConditionOperator.Equal,dokument.Id)
                        }
                    }
                };
                List<BE.annotation> annotations = _base.Service.RetrieveMultiple<BE.annotation>(queryAnno);
                if (annotations.Count == 0) {
                    var client1 = new RestClient("https://api.signi.com/api/v1/contract/sign/template")
                    {
                        Timeout = -1
                    };
                    //client.UseNewtonsoftJson();
                    var request1 = new RestRequest(Method.POST);
                    //request.UseNewtonsoftJson();
                    request1.AddHeader("x-api-key", "71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f");
                    request1.AddHeader("Content-Type", "multipart/form-data");
                    request1.AddParameter("email_author", sablona.art_emailautor);
                    request1.AddParameter("email_signer", contact.emailaddress1);
                    request1.AddParameter("phone_signer", contact.mobilephone);
                    request1.AddParameter("firstname_signer", contact.firstname);
                    request1.AddParameter("lastname_signer", contact.lastname);
                    request1.AddParameter("proposer_sign", "true");
                    request1.AddParameter("negotiator_sign", "true");
                    request1.AddParameter("contract_name", dokument.art_name);
                    request1.AddParameter("contract_number", dokument.art_cislodokumentu);
                    request1.AddParameter("sign_date", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
                    request1.AddParameter("sign_place", sablona.art_mistopodpisu);
                    request1.AddParameter("last_document", "true");
                    request1.AddParameter("person_type", "legal");
                    //request.AddParameter("date_of_birth", "1970-04-16");
                    //request.AddParameter("street", "5. května 67");
                    //request.AddParameter("city", "Libčice nad Vltavou");
                    //request.AddParameter("zip_code", "25266");
                    request1.AddParameter("company_name", sablona.art_spolecnost);
                    //request.AddParameter("ic", "28524322");
                    //request.AddParameter("dic", "CZ28524322");
                    //request.AddParameter("sign_proposer", "{\"position\": {\"x\": 15,\"y\": 84,\"page\": 0}}");
                    //request.AddParameter("sign_negotiator", "{\"position\": {\"x\": 78,\"y\": 84,\"page\": 0}}");
                    //request.AddParameter("sign_proposer[position][x]", 8);
                    //request.AddParameter("sign_proposer[position][y]", 84);
                    //request.AddParameter("sign_proposer[position][page]", sablona.art_stranapospisu - 1);
                    //request.AddParameter("sign_negotiator[position][x]", 64);
                    //request.AddParameter("sign_negotiator[position][y]", 84);
                    //request.AddParameter("sign_negotiator[position][page]", sablona.art_stranapospisu - 1);
                    request1.AddParameter("webhooks[0][state]", "signed");
                    request1.AddParameter("webhooks[0][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookSigned");
                    request1.AddParameter("webhooks[1][state]", "rejected");
                    request1.AddParameter("webhooks[1][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookRejected");
                    request1.AddParameter("webhooks[2][state]", "expired");
                    request1.AddParameter("webhooks[2][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookExpired");
                    request1.AddParameter("template_id", 170);
                    request1.AddParameter("parameters", JsonConvert.SerializeObject( new[] { new { id = 411, value = contact.description } }));
                    //request1.AddParameter("parameters[0][id]", 411);
                    //request1.AddParameter("parameters[0][value]", contact.description);
                    IRestResponse response1 = client1.Execute(request1);
                    if (response1.StatusCode == System.Net.HttpStatusCode.OK)
                    {
                        BE.art_idokument iDokumentUpdate = new BE.art_idokument
                        {
                            Id = dokument.Id,
                            art_id = JsonConvert.DeserializeObject<UploadDokumentResponseClass>(response1.Content).ContractId,
                            art_idokumentid = dokument.Id,
                            statuscode = new OptionSetValue(BE.art_idokument.Attr.statuscode.Options.A_Odeslany_k_podpisu)
                        };
                        _base.Trace = true;
                        _base.Debug(iDokumentUpdate, "iDokumentUpdate");


                        iDokumentUpdate.Update(_base.Service);
                        return response1.Content;
                    }
                    return "Žádné přílohy";
                }
                
                BE.annotation anno = annotations[0];
                byte[] bytes = Convert.FromBase64String(anno.documentbody);
                //if (anno.filename.Contains("html"))
                //{
                //    byte[] data = Convert.FromBase64String(anno.documentbody);
                //    string decodedString = Encoding.UTF8.GetString(data);
                //    string newBodyText = decodedString.Replace("{data_z_dynamics1}", contact.description).Replace("{data_z_dynamics2}", contact.address1_line1);
                //    bytes = Encoding.UTF8.GetBytes(newBodyText);
                //}

                // Dokument stažený z Dynamics 365 DB
                var client = new RestClient("https://api.signi.com/api/v1/contract/sign/provided")
                {
                    Timeout = -1
                };
                //client.UseNewtonsoftJson();
                var request = new RestRequest(Method.POST);
                //request.UseNewtonsoftJson();
                request.AddHeader("x-api-key", "71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f");
                request.AddHeader("Content-Type", "multipart/form-data");
                request.AddParameter("email_author", sablona.art_emailautor);
                request.AddParameter("email_signer", contact.emailaddress1);
                request.AddParameter("phone_signer", contact.mobilephone);
                request.AddParameter("firstname_signer", contact.firstname);
                request.AddParameter("lastname_signer", contact.lastname);
                request.AddParameter("proposer_sign", "true");
                request.AddParameter("negotiator_sign", "true");
                request.AddParameter("contract_name", dokument.art_name);
                request.AddParameter("contract_number", dokument.art_cislodokumentu);
                request.AddParameter("sign_date", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
                request.AddParameter("sign_place", sablona.art_mistopodpisu);
                request.AddParameter("last_document", "true");
                request.AddParameter("person_type", "legal");
                //request.AddParameter("date_of_birth", "1970-04-16");
                //request.AddParameter("street", "5. května 67");
                //request.AddParameter("city", "Libčice nad Vltavou");
                //request.AddParameter("zip_code", "25266");
                request.AddParameter("company_name", sablona.art_spolecnost);
                //request.AddParameter("ic", "28524322");
                //request.AddParameter("dic", "CZ28524322");
                //request.AddParameter("sign_proposer", "{\"position\": {\"x\": 15,\"y\": 84,\"page\": 0}}");
                //request.AddParameter("sign_negotiator", "{\"position\": {\"x\": 78,\"y\": 84,\"page\": 0}}");
                request.AddParameter("sign_proposer[position][x]", 8);
                request.AddParameter("sign_proposer[position][y]", 84);
                request.AddParameter("sign_proposer[position][page]", sablona.art_stranapospisu - 1);
                request.AddParameter("sign_negotiator[position][x]", 64);
                request.AddParameter("sign_negotiator[position][y]", 84);
                request.AddParameter("sign_negotiator[position][page]", sablona.art_stranapospisu - 1);
                request.AddParameter("webhooks[0][state]", "signed");
                request.AddParameter("webhooks[0][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookSigned");
                request.AddParameter("webhooks[1][state]", "rejected");
                request.AddParameter("webhooks[1][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookRejected");
                request.AddParameter("webhooks[2][state]", "expired");
                request.AddParameter("webhooks[2][url]", "https://artidocumentswebapi.azurewebsites.net/IDocument/WebHookExpired");
                request.AlwaysMultipartFormData = true;
                request.AddFileBytes("file", bytes, anno.filename, anno.mimetype);
                IRestResponse response = client.Execute(request);
                if (response.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    BE.art_idokument iDokumentUpdate = new BE.art_idokument
                    {
                        Id = dokument.Id,
                        art_id = JsonConvert.DeserializeObject<UploadDokumentResponseClass>(response.Content).ContractId,
                        art_idokumentid = dokument.Id,
                        statuscode = new OptionSetValue(BE.art_idokument.Attr.statuscode.Options.A_Odeslany_k_podpisu)
                    };
                    _base.Trace = true;
                    _base.Debug(iDokumentUpdate, "iDokumentUpdate");


                    iDokumentUpdate.Update(_base.Service);
                    return response.Content;
                }
                return response.Content;
            }
            catch (Exception e)
            {
                _base.Error(e, "Art.IDocuments.Logic.AnnotationLogic.Upload");
                return e.Message;
            }
        }

// Download Document

        public void DownloadTest()
        {
            BE.annotation anno = (BE.annotation)_base.Service.Retrieve(BE.annotation.EntityLogicalName, new Guid("54DC0A3F-75A5-EA11-A812-000D3AB7AF7A"), new ColumnSet(true));
            RestClient client = new RestClient("https://api.signi.com/api/v1/contract/2842/download");
            client.AddDefaultHeader("x-api-key", "71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f");
            client.AddDefaultHeader("Content-Type", "application/json");

            IRestRequest request = new RestRequest
            {
                Method = Method.GET
            };
            IRestResponse response = client.Get(request);
            string fileBase64 = Convert.ToBase64String(response.RawBytes); //fileBase64 - serializovaný soubr, který je možné uložit do DB
        }

        public string Download(EntityReference dokumentReference)
        {
            try
            {
                string output = "OK";

                BE.art_idokument dokument = _base.Service.Retrieve(BE.art_idokument.EntityLogicalName, dokumentReference.Id,
    new ColumnSet(BE.art_idokument.Attr.art_customerid.LogicalName, BE.art_idokument.Attr.art_name.LogicalName, BE.art_idokument.Attr.art_sablonaid.LogicalName, BE.art_idokument.Attr.art_id.LogicalName)).ToEntity<BE.art_idokument>();

                QueryExpression queryAnno = new QueryExpression
                {
                    EntityName = BE.annotation.EntityLogicalName,
                    NoLock = true,
                    TopCount = 1,
                    ColumnSet = new ColumnSet(BE.annotation.Attr.filename.LogicalName),
                    Criteria = new FilterExpression(LogicalOperator.And)
                    {
                        Conditions =
                        {
                            new ConditionExpression(BE.annotation.Attr.objectid.LogicalName,ConditionOperator.Equal,dokument.Id)
                        }
                    }
                };
                List<BE.annotation> annotations = _base.Service.RetrieveMultiple<BE.annotation>(queryAnno);
                if (annotations.Count == 0) return "Žádné přílohy";
                BE.annotation anno = annotations[0];


                RestClient client = new RestClient($"https://api.signi.com/api/v1/contract/{dokument.art_id}/download");
                client.AddDefaultHeader("x-api-key", "71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f");
                client.AddDefaultHeader("Content-Type", "application/json");

                IRestRequest request = new RestRequest
                {
                    Method = Method.GET
                };
                IRestResponse response = client.Get(request);
                string fileBase64 = Convert.ToBase64String(response.RawBytes);
                BE.annotation annotation = new BE.annotation
                {
                    objectid = dokumentReference,
                    documentbody = fileBase64,
                    subject = $"Podepsaný dokument: {dokument.art_name}",
                    filename = $"{anno.filename}_podpis.pdf"
                };
                annotation.Create(_base.Service);
                return output;
            }
            catch (Exception e)
            {
                _base.Error(e, "Art.IDocuments.Logic.AnnotationLogic.Download");
                return e.Message;
            }
        }

    }
}

Příklad kódu v VBA

Visual Basic for Application je vývojový jazyk používaný v desktopových verzích Microsoft Office . Není již podporován v cloudovém prostředí Office 365.

  • Na produkčním prostředí Signi je workspace "Demo API" a má API klíč = “71c4123d242bdd38047bee838d17e3367dc3ea6748d0975217ce501e834a224c83cab8afd35c9b0e6ade806b7987fae80f97f5c8253cfbb9089cf21f” , při svých voláních zaměňte tento API klíč za API klíč svého workspace, který získáte po objednání služby "Integrace API" a Generování API klíče.

  • Je třeba kód spouštět Micrisoft Excel for Desktop na MS Windows. Na cloudovém prostředí MS 356 nefugnují makrave VBA, v Microsoft Execl for Mac není přístupný objekt pro HTTP requesty.

Code Block
Sub FillTemplateAndSend()

  Dim URL As String
  Dim JSONString As String
  Dim RowString As String
  Dim objHTTP As New WinHttpRequest
  Dim ConfProposerEmail As String
  Dim ConfAPIKey As String
  Dim ConfMapping As String
  Dim ConfURL As String
  Dim i As Integer
  Dim ContractFrom As Integer
  Dim ContractTo As Integer
  Dim Contracts As Range
  Dim SigniID As String
  Dim ResponseText As String
  Dim TemplateNo As Integer
  Dim TemplateRow As Integer
  
  'Načtení parametrů konfigurace
  
  ConfURL = "https://api.signi.com/api/v1/contract/?type=template"
  ConfAPIKey = Worksheets("Config").Range("C6").Value
  ContractFrom = 5 'první řádek na kterém jsou data
  ContractTo = Worksheets("Data").UsedRange.Rows.Count  'poslední řádek na kterém jsou data
 
  objHTTP.Open "POST", ConfURL, False
  objHTTP.SetRequestHeader "x-api-key", ConfAPIKey
  objHTTP.SetRequestHeader "Content-Type", "application/json"

  'procházení řádků , posílání do Signi a aktualizace řádků
  
  For i = ContractFrom To ContractTo
    If UCase(Worksheets("Data").Range("A" & i).Value) = "ODESLAT" Then
        TemplateNo = Worksheets("Data").Range("B" & i).Value
        If IsNumeric(TemplateNo) Then
        RowString = Worksheets("Config").Range("C" & (TemplateNo + 10)).Value
        RowString = Replace(RowString, "#proposeremail#", Worksheets("Config").Range("C7").Value)
        RowString = Replace(RowString, "#state#", Worksheets("Config").Range("C8").Value)
        RowString = Replace(RowString, "#locale#", Worksheets("Config").Range("C9").Value)
    
    
        If TemplateNo = "1" Then
            RowString = Replace(RowString, "#contract_name#", Worksheets("Data").Range("E" & i).Value)
            RowString = Replace(RowString, "#number#", Worksheets("Data").Range("F" & i).Value)
            RowString = Replace(RowString, "#firstname#", Worksheets("Data").Range("G" & i).Value)
            RowString = Replace(RowString, "#lastname#", Worksheets("Data").Range("H" & i).Value)
            RowString = Replace(RowString, "#email#", Worksheets("Data").Range("I" & i).Value)
            RowString = Replace(RowString, "#phone#", Worksheets("Data").Range("J" & i).Value)
            RowString = Replace(RowString, "#112#", Worksheets("Data").Range("K" & i).Value)
            RowString = Replace(RowString, "#122#", Worksheets("Data").Range("L" & i).Value)
            RowString = Replace(RowString, "#131#", Worksheets("Data").Range("M" & i).Value)
            RowString = Replace(RowString, "#411#", Worksheets("Data").Range("N" & i).Value)
            RowString = Replace(RowString, "#421#", Worksheets("Data").Range("O" & i).Value)
            RowString = Replace(RowString, "#431#", Worksheets("Data").Range("P" & i).Value)
        End If
                
        objHTTP.Send RowString
        ResponseText = objHTTP.ResponseText
        ResponseText = Replace(ResponseText, "{""contract_id"":", "")
        If InStr(ResponseText, """attachments""") >= 2 Then
          SigniID = Left(ResponseText, InStr(ResponseText, """attachments""") - 2)
        Else
          SigniID = ResponseText 'pokud chybí text "attachements", předpokládá se, že došlo k chybě
        End If
        Worksheets("Data").Range("D" & i) = RowString ' JSON použitý pro volání
        Worksheets("Data").Range("C" & i) = SigniID
        Worksheets("Data").Range("A" & i).Value = "Odesláno"
     End If
     End If
  Next i
Dim Z As String

End Sub



Sub ReadStatus()
  Dim ConfURL As String
  Dim ConfAPIKey As String
  Dim URL As String
  Dim SigniID As String
  Dim ResponseText As String
  Dim i As Integer
  Dim ContractFrom As Integer
  Dim ContractTo As Integer
  Dim objHTTP As New WinHttpRequest
  
  
  
  'Načtení parametrů konfigurace
  
  ConfAPIKey = Worksheets("Config").Range("C6").Value
  ConfURL = "https://api.signi.com/api/v1/contract/"
  ContractFrom = 5
  ContractTo = Worksheets("Data").UsedRange.Rows.Count
 
  'procházení řádků , posílání do Signi a aktualizace řádků
  
  For i = ContractFrom To ContractTo
    SigniID = Worksheets("Data").Range("B" & i).Value 'Načtení SigniID ze sloupce D
    If IsNumeric(SigniID) And Not (IsEmpty(SigniID)) Then 'když je hodnota numerická a neprázdná tj.j. je v ní pravděpodobně smysluplné ContractID
        URL = ConfURL & SigniID
        
        objHTTP.Open "GET", URL, False
        objHTTP.SetRequestHeader "x-api-key", ConfAPIKey
        objHTTP.SetRequestHeader "Content-Type", "application/json"

        
        objHTTP.Send
        ResponseText = objHTTP.ResponseText
        If InStr(ResponseText, """pending""") > 0 Then
            Worksheets("Data").Range("A" & i).Value = "Rozesláno"
        ElseIf InStr(ResponseText, """completed""") > 0 Then
            Worksheets("Data").Range("A" & i).Value = "Podepsáno"
        ElseIf InStr(ResponseText, """expired""") > 0 Then
            Worksheets("Data").Range("A" & i).Value = "Uplynula doba"
        ElseIf InStr(ResponseText, """refused""") > 0 Then
            Worksheets("Data").Range("A" & i).Value = "Odmítnuto"
        End If
        
     End If
  Next i



End Sub