EasyUBL Api is the programmer's tool. Here you will find the functions that work with the ERP system as the active party. EasyUBL responds back with webhooks. In many cases, it will be appropriate to build a middleware application that responds to the webhooks EasyUBL sends. |
If you do not have a tenant, you can work directly in the sandbox. You should not create "real" companies in the sandbox, as all other users can see your data. It will be announced on the Peppol network that the companies you create can receive UBL documents and these will be sent to EasyUBL's sandbox for everyone to see. The sandbox contains two companies registered on the Peppol network. You can try to send documents between these companies.
If you want to work with your own data, you must contact EasyUBL to receive a Tenant key. If you create a company with this key, you are on the peppol network immediately, and you will immediately start receiving documents that you can see in the bookkeepers page. On the bookkeepers page you can see the companies you create and the invoices you receive. This way you can work directly in the API and see the result. The credentials for the sandbox is User: SandboxAdmin Password: SandboxAdmin! control code: 0000    Along with your own tenant key, you will receive login credentials for this tenant's administrator. |
Before you can send/receive, you must create a company
API/Company/AddUpdateCompany |
HttpClient myClient = new HttpClient();
myClient.DefaultRequestHeaders.Add("Authorization", ApiKey); string myUrl = String.Concat("https://EasyUBL.net/api/Company/Update/", CompanyId); string myCompanyJson = JsonConvert.SerializeObject(myCompany); var stringContent = new StringContent(myCompanyJson, UnicodeEncoding.UTF8, "application/json"); var response = await myClient.PostAsync(myUrl, stringContent); response.EnsureSuccessStatusCode(); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); string sub = (string)JsonConvert.DeserializeObject(responseContent); CompanyId = Guid.Parse(sub); // save the company guid in your application } |
- |
public class companyEasyUBL { public string name { get; set; } public string cvr { get; set; } public string currency { get; set; } public string country { get; set; } public string? webhookUrl { get; set; } public addEndpoint defaultEndpoint { get; set; } public addAddress defaultAddress { get; set; } public addContact defaultContact { get; set; } public addPayment payment { get; set; } public bool? DoNotReceiveUBL { get; set; } = false; } public class addEndpoint { public string endpointType { get; set; } public string endpointIdentifier { get; set; } } public class addAddress { public string? name { get; set; } public string? streetName { get; set; } public string? additionalStreetName { get; set; } public string? buildingNumber { get; set; } public string? inhouseMail { get; set; } public string? department { get; set; } public string? attensionName { get; set; } public string? cityName { get; set; } public string? postalCode { get; set; } public string? countrySubentity { get; set; } public string? countryCode { get; set; } } public class addContact { public string roleCode { get; set; } public string name { get; set; } public string email { get; set; } public string sms { get; set; } } public class addPayment { public string bankname { get; set; } public string bankRegNo { get; set; } public string bankAccount { get; set; } public string bic { get; set; } public string iban { get; set; } public string CreditorIdentifier { get; set; } } |
The preferred way to send an invoice
Sending invoices and receiving notifications should be the easy way to handle an invoice. The possibilities depend entirely on the extent to which it is possible to make changes to the ERP system. The integration with EasyUBL is the easy part. Don't underestimate the importance of handling notifications. |
Invoices are sent with the feature /api/ SendDocuments/ Invoice/ {companyId} The function demands "companyId" as a parameter to identify the sender company address (the supplier). This ensures that no one sends invoices on your behalf through EasyUBL. This information is found in your database after calling AddUpdate (create companies). The function SendDocuments/Invoice" sends the invoice and returns the complete UBL document. |
HttpClient myClient = new HttpClient(); myClient.DefaultRequestHeaders.Add("Authorization",myTenantKey); string myUrl = String.Concat("https:// EasyUBL.net/api /SendDocuments/ Invoice/", companyId); string myInvoiceJson = JsonConvert.SerializeObject(myInvoice); var stringContent = new StringContent(myInvoiceJson, UnicodeEncoding.UTF8, "application/json"); var response = await myClient.PostAsync(myUrl, stringContent); response.EnsureSuccessStatusCode(); myXmlDocument = await response.Content.ReadAsStringAsync(); |
You should archive the complete UBL document in the database. This is the legal document the recipient receives. In the event of disputes, the complete UBL document is what must be referred to. Human readable versions formed on the fly like PDF, HTML etc. must be
considered "copies with special features" like being human readable, but they are not the legal document. That is solely the actual complete UBL document. Some recipients have special requirements for the content, that must be agreed with the invoice issuing company. For example, this could be a requisition number, specific contact persons, accounting codes, cost centers etc. If the recipient has an elaborate system, a message will be returned, if any of these informations are missing. If the recipient behaves correctly, this message will indicate, where in the UBL document this information must be placed. This means that you will be notified very quickly after sending a document, and you can then add any missing information, if that's the case, and resend it right away, not having to hunt down someone at the sender, who knows where the information should be found in the document. Furthermore, the UBL document is the legal document. In the event of disputes, this is what must be referred to. Human readable versions can be considered copies with special abilities that are formed on the fly. |
The document may be built in this way. This is only an example to illustrate the scope. Data is read from an SQL server and placed in the json document. |
SqlDataReader myr = Comm.ExecuteReader(); if (myr.Read()) { int AddressID = (int)myr["So_addressID"]; if (AddressID == 0) errCode = 1; int CreInvFactor = (int)myr["CreInvFactor"]; if (CreInvFactor == -1) myI.invoiceCreditnote = "Cre"; else myI.invoiceCreditnote = "Inv"; myI.id = new(myr["InvoiceNo"].ToString()); myI.documentCurrencyCode = myr["Currency"].ToString().ToUpper(); myI.issueDate = (myr["InvDate"].Equals(DBNull.Value) ? DateTime.Today : (DateTime)myr["InvDate"]); myI.dueDate = (myr["PayDate"].Equals(DBNull.Value) ? DateTime.Today : (DateTime)myr["PayDate"]); myI.accountingCost = myr["AccountingCost"].ToString(); myI.buyerReference = myr["requisition"].ToString(); long OrderNo; long.TryParse(myr["OrderNo"].ToString(), out OrderNo); myI.salesOrderID = OrderNo.ToString(); myI.note = string.Concat(myr["text_1"], " ", myr["text_2"]).Trim(); if (myr["ShipDate"].Equals(DBNull.Value)) myI.deliveryDate = myI.issueDate; else myI.deliveryDate = (DateTime)myr["ShipDate"]; int thisContId = myr["ContID"] == DBNull.Value ? 0 : (int)myr["ContID"]; myI.accountingCustomerParty = GetPartyInfo((int)myr["So_addressID"]); myI.accountingCustomerParty.Contact = GetContactInfo((int)myr["So_addressID"], thisContId); if (string.IsNullOrEmpty(myI.accountingCustomerParty.Contact.name)) { myI.accountingCustomerParty.Contact.name = myI.accountingCustomerParty.name; } myI.totalAmount = (decimal)myr["amount"]; myI.accountingCustomerParty.postalAddress = GetPostalAddress((int)myr["Sh_addressID"]); if ((int)myr["Sh_addressID"] != 0) myI.deliverAddress = GetPostalAddress((int)myr["Sh_addressID"]); myI.invoiceLines = GetLines(saleID); } return myI; |
It is a traditional mapping of the fields found in the local database for the JSON document describing the invoice. Enjoy the fact that it is significantly easier to map for this document than to the underlying XML document.
Feel free to use the source text for this application. If you turn to Easyubl support, we will be happy to help with advice and guidance. |
A real-time and seamless integration
To receive a webhook, you must have an api that can service all of your tenant's companies.
EasyUBL webhook calls a url, that you provide when creating your tenant If you create multiple Webhook recipients, EasyUBL will send the same notifications to all of them. For example, you can send notifications about address creations to your ERP system, your webshop, your CRM system, etc. EasyUBL sends webhooks all the time. You don't need to respond to them. We will resend a notification a certain number of times before giving up. We send the notifications we receive from the UBL network to you as WebHooks. In addition, we send events from the EasyUBL applications. It could be: https://myBasicURL/api/ tools/ UBLNotifications The webhook call to your service will contain a small json document: { "companyId": "c9b89d84-cdb6-4207-8a99-23c7ffb04577", "endpointId": "df4967d4-7e4d-4f23-9653-b8bd1b8719b2", "ublDocumentId": "22323456", "documentStatusCode" : "5110", "documentType": "110", "actionCode": "2", "errorCode": "-1", "base64EncodedMessage": "Not sent", "externalIdentifier": "5053387" "documentXmlBase64Content" } CompanyId will make it possible to find the company in your application. This is the key that was returned, when you created the company. You probably don't use EndpointId for anything. ublDocumentID is an internal ID in the system, that makes it possible to backtrace the document. You don't use it. externalIdentifier will contain your invoice number/order number etc. depending on the document type. We currently support document types 100 (order), 110 (Invoice), 120 (credit note) and Essy100 (Financial documents and goods transactions). "base64MEssage" and "documentBase64" will contain information related to the document type. This is an example of what a webhook might look like. |
[Route("UBLNotifications")] [HttpPost] public int UBLNotifications(Notification ublNotification) {   if (ublNotification.companyId == Guid.Empty) throw new ArgumentException("Missing companyId");   DBUser.ConnectionGetByEasyUBLKey(ublNotification.companyId);   string DocumentType = ublNotification.documentType;   if (DocumentType == "110" || DocumentType == "120") // invoice/ creditnote   {    NotificationService myUBL = new NotificationService(DBUser);    if (ublNotification.documentStatusCode < 5200)    {    String message = myUBL.UpdateInvoice(ublNotification);    }   }   if (DocumentType == "100")   {    NotificationOrderService myUBL = new NotificationOrderService(DBUser);    if (ublNotification.documentStatusCode < 5200)    {     String message = myUBL.UpdateOrder(ublNotification);    }   }   if (DocumentType == "EasyUBLDocumentBooked")   {    NotificationVoucherService myUBL = new NotificationVoucherService(DBUser);    String message = myUBL.ProcessVoucher(ublNotification);    }   if (DocumentType == "EasyUblProductChanged")  {   EasyUblProductService myUBL = new EasyUblProductService(DBUser);   String message = myUBL.OnProductChanged((ublNotification));  }  if (DocumentType == "EasyUblDocumentReceived")  {   NotificationVoucherService myUBL = new NotificationVoucherService(DBUser);   String message = myUBL.OnDocumentReceived((ublNotification));  }   return 0; } |
A document with a document type of 5200 and above is for receipt. This is handled by the "Bookkeepers Page". Notifications below 5200 contain information about the document's shipment. This information can be used to update directly in the ERP system.
A document with DocumentType "EasyUBLDocumentBooked" is a document that must be saved in the ERP system. The call is made by Bookkeepers Page when the paper flyer is clicked that transfers the document to the journal. The document that is delivered is similar to that returned by the API called "NextTransaction". The call makes it possible to transfer vouchers to the ERP system in real time. the field "documentXmlBase64Content" will contain at json document: EasyUblDocumentReceived contains the same document as "EasyUBLDocumentBooked" but with less information. It is the result of the first attempt to interpret the document. The call comes when the document arrives at EasyUBL. The document contains, among other things, the creditor's address, and can be used to create the creditor in the ERP system. It will be convenient that the creditor exists when the invoice arrives. EasyUblProductChanged contains a document with product information. The call is sent every time an item is changed in EasyUBL and is used to maintain products in the ERP system. EasyUBL will attempt to map suppliers' products to the buyer's and, for example, convert units and packages if the necessary information is found in the UBL document. |
We talk to everyone
Webhooks from EasyUBL can either be sent to a central API, or to a locally installed middleware belonging to a single company. This middleware must contain a single API call that receives webhooks from EasyUBL. If it works directly against the locally installed database, the system will work in real time.
If the machine the database is installed on is visible from the web, and you have the ability to write to the database, we can communicate this way. If you wish, we can send you an example written in C# of an API that receives webhooks from EsyUBL. You then write the part that communicates with the database. The alternative methods are not described here, but you are welcome to contact EasyUBL if you need alternatives. We can usually find a solution. |
Notifications
Numeric document types are the standard UBL document type. These arrive when the document is accepted. The field "documentXmlBase64Content" contains the xml document as we receive it. We do not sort the documents so you will receive the documents you are registered to receive.
Documenttype 100: Order, 110: Invoice, 120 Creditnote. If "documentStatusCode" contains a value less than 5200, it is a notification that provides a status for a sent document. The status message is in "base64EncodedMessage". The function enables the sender to continuously follow the document's path to the recipient and finally receive a receipt for receipt. |
Send documents
If "documentStatusCode" contains the value less than 5200, it is a notification that a sent document is on its way out. The "base64EncodedMessage"" field will contain a description of the document's status.
Before the document is sendt, thise codes may occur: 100: Waiting for robot. This means that the document has been queued but not yet sent. 110: Sender company not found. There may be missing information on the company (endpoint, company no, country code, currency, e-mail etc.) This may be because EasyUBL cannot create the company because information is missing. 111: The recipient: xxxxx is not registered to receive. The recipient must be registered to receive UBL documents. Otherwise there is no point in trying to send the document. While the document is in transit, these codes are received. This list may not be complete as changes can be made without EasyUBL needing to know. 5101: Document not sent The reason is described in the error message. 5110: Pending The document is awaiting validation and sending. 5120: Pending after validation The document has been validated. If the validation had failed, a code 5101 would have been sent instead with the validation result in the error message. 5130: Sending The document is located somewhere between the sender and the recipient. 5140: Send This means that the recipient's system has acknowledged receipt. This does not necessarily mean that the recipient has received the document themselves, but that the responsibility for delivery lies with the recipient's system. 5150: Received The recipient's own ERP system has accepted the receipt. They have not necessarily accepted the document but they have received it. This status is only achieved if the recipient's own approval flow supports this feature. 5160: Confirmed The recipient has accepted the document. This status is only achieved if the recipient's own approval flow supports this feature. 5170: Rejected The recipient has rejected the document. This status is only achieved if the recipient's own approval flow supports this feature. |
Receive documents
If "documentStatusCode" contains the value 5210, it is a notification that a document has arrived. The document is found in the "documentXmlBase64Content" field. All document types are received in this way.
The "ublDocumentId" field is unique for each document. Use this to avoid receiving the same document multiple times. |
Processed documents
The documents can be processed in the EasyUBL applications. The result is received in a webhook.
EasyUBLDocumentBooked: Is fired when you press post in the Bookkeeper´s page application. The "documentXmlBase64Content" field contains the finished document that must be submitted to the ERP system's draft along with the original xml document and the human-readable formats html and pdf. EasyUblDocumentReceived: Is fired when EasyUBL processes a received document. The "documentXmlBase64Content" field contains an incomplete document, which can be used, for example, to create a new vendor in the ERP system. This will make it easier to process new vendors if they are already known in the ERP system when the invoice is requested. You can also use the function to maintain vendor addresses. EasyUblOrderReceived: Is fired when an order is completed in the application Consignor´s page approves an order for receipt. In Consignor´s page, an order can be adjusted for product numbers, units, delivery times, etc. The adjusted order is expected to be created in the ERP system. EasyUblProductChanged: Is fired when a product is changed in the Bookkeeper´s page and Consignor´s page. This opens the possibility to maintain product information manually in EasyUBL, import catalog information or perhaps inherit information from a supplier. |