Version: 1.2
Date: Nov 30, 2023
The API enables seamless integration with applications not included in our pre-built options, allowing clients to connect and automate workflows tailored to their unique needs.
Table of Contents
- Overview
- Authentication/Authorization
- Summary of All Available API Controllers
- Examples of API Controller’s Request-Response
Overview
Xink provides REST API for external clients. All traffic between a client application and Xink service is transported by encrypted HTTP requests and responses.
API service URI:
https://e-{dc}.xink.io/
API service URI for US DC:
https://e.xink.io/
Authentication/Authorization
We use OAuth2 to control access to Xink APIs, external clients should use ClientCredentialsGrant in particular. Client should request access token from Xink authorization server, then use the token in all subsequent requests to the API.
In Xink admin portal you need to create an API user. Navigate to preferences->admins and add an API user. Give the user proper permissions and a good password. The user referred to in this documentation is an API user.
Authorization server URI:
https://auth.xink.io
Authorization endpoint:/OAuth/Authorize
Token endpoint:/OAuth/Token
// C# example
var authorizationServerUri = new Uri("https://auth.xink.io");
var authorizationServer = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri(authorizationServerUri, "/OAuth/Authorize"),
TokenEndpoint = new Uri(authorizationServerUri, "/OAuth/Token")
};
_webServerClient = new WebServerClient(authorizationServer, "some-id-here@SRV", "secretpassword");
var state = _webServerClient.GetClientAccessToken(new[] { "employeeread", "employeesignaturesread" });
_accessToken = state.AccessToken;
Please note scope names employeeread
and employeesignaturesread
, these are required to get employee list and signatures. Each operation may require some scope.
By default token is valid for 2 hours since authorization request. On later stages, this value might be changed to 10-15 minutes.
Summary of All Available API Controllers
Account
[HttpGet, Route("api/1.0/account/info")]
Employee
[HttpGet, Route("api/1.0/employee/list/{offset:int=0}/{limit:int=500}")]
[HttpGet, Route("api/1.0/employee/{email}/fields")]
[HttpGet, Route("api/1.0/employee/{email}/signatures")]
[HttpPost, Route("api/1.0/employee/upload")]
[HttpPost, Route("api/1.0/employee/delete")]
Image
[HttpPost, Route("api/1.0/image")]
[HttpGet, Route("api/1.0/image/list/{offset:int=0}/{limit:int=500}")]
Signature
[HttpGet, Route("api/1.0/signature/list/{offset:int=0}/{limit:int=500}")]
[HttpPost, Route("api/1.0/signature/")]
[HttpPost, Route("api/1.0/signature/{id}/update")]
[HttpDelete, Route("api/1.0/signature/{id}")]
[HttpPost, Route("api/1.0/signature/{id}/delete")]
Signature Import
[HttpPost, Route("api/1.0/signatureimport/{id}/image")]
[HttpPost, Route("api/1.0/signatureimport/{id}")]
Rerouting
[HttpGet, Route("api/1.0/rerouting/status")]
[HttpPost, Route("api/1.0/rerouting/clearmap")]
[HttpPost, Route("api/1.0/rerouting/replacemap")]
Managed Accounts
[HttpGet, Route("/api/1.0/managedAccounts/list")]
Examples of API Controller’s Request-Response
Scopes
employeeread
employeefieldsread
employeectrl
(WRITE)employeesignaturesread
signaturectrl
(WRITE)managedaccountsread
Account
[HttpGet, Route("api/1.0/account/info")]
Scope: employeeread
{
"CompanyName": "Some Company",
"LicensedEdition": "Xink Campaign",
"LicensedCount": 6,
"CreatedDate": "2013-02-07T07:37:52.78",
"RenewalDate": null,
"InvoiceEmail": "some@some.com",
"TotalEmployees": 530,
"Signatures": 32,
"DefaultSignature": null,
"ReplySignature": null,
"RunningCampaigns": [
{
"Name": "disclaimer2",
"ContentText": null,
"ContentHTML": "disclaimer2",
"Active": true,
"TrackClicks": true,
"Weight": 2,
"Notes": null,
"EffectiveActive": true,
"SignaturesCount": 0,
"ClickCount": 0,
"ViewCount": 8
},
. . .
{
"Name": "Subscribe",
"ContentText": null,
"ContentHTML": "<a href=\"https://subscribe.some.com/subscribe-feeds.aspx?utm_medium=email&utm_campaign=NewsSubscribers&utm_source=email%20signature\"><img alt=\"\" border=\"0\" height=\"151\" src=\"/Images/Get/G3/p83.gif\" width=\"300\" /></a>",
"Active": true,
"TrackClicks": true,
"Weight": 1,
"Notes": null,
"EffectiveActive": true,
"SignaturesCount": 1,
"ClickCount": 11,
"ViewCount": 15
}
],
"UpcomingCampaigns": [],
"TopCampaigns": [],
"DisabledCampaigns": [
{
"Name": "__custom_tracking_link_01",
"ContentText": null,
"ContentHTML": null,
"Active": false,
"TrackClicks": true,
"Weight": 1,
"Notes": null,
"EffectiveActive": false,
"SignaturesCount": 1,
"ClickCount": 0,
"ViewCount": 53
},
. . .
{
"Name": "new campaign",
"ContentText": null,
"ContentHTML": null,
"Active": false,
"TrackClicks": true,
"Weight": 1,
"Notes": null,
"EffectiveActive": false,
"SignaturesCount": 0,
"ClickCount": 0,
"ViewCount": 0
}
],
"DateFrom": "2023-07-09T00:00:00+00:00"
}
Employee
[HttpGet, Route("api/1.0/employee/list/{offset:int=0}/{limit:int=500}")]
Example: /api/1.0/employee/list/0/3
Response contains Offset and Limit parameters values used in the request (or defaults if no parameters were supplied); and actual array of Items, each represents one employee.
Scope: employeeread
{
"Limit": 500,
"Offset": 0,
"SearchQuery": null,
"Items": [
{
"Email": "john@doe.com",
"FirstName": "John",
"LastName": "Doe",
"DisplayName": "Jonny D"
},
{
"Email": "maria@doe.com",
"FirstName": "Maria",
"LastName": "Doe",
"DisplayName": "Maria D"
},
. . .
[HttpGet, Route("api/1.0/employee/{email}/fields")]
Scope: employeefieldsread
{
"Fields": [
{
"Code": "Country",
"DisplayName": "Country",
"FieldValue": null,
"IsImage": false
},
{
"Code": "DisplayName",
"DisplayName": "Display Name",
"FieldValue": "somename",
"IsImage": false
},
. . .
{
"Code": "LastName",
"DisplayName": "Last Name",
"FieldValue": "somename",
"IsImage": false
},
{
"Code": "Email",
"DisplayName": "Employee's Email",
"FieldValue": "test@some.com",
"IsImage": false
},
. . .
[HttpGet, Route("api/1.0/employee/{email}/signatures")]
Scope: employeesignaturesread
{
"Clean": false,
"SetDefault": true,
"SetReply": false,
"Signatures": [
{
"Name": "",
"HTML": null,
"Text": null,
"DefaultNew": false,
"DefaultReply": true
},
{
"Name": "(KM) 3-4-Sterne Offerte",
"HTML": "<p style=\"font-family:arial,helvetica,sans-serif; font-size:9pt !important;\"><img alt=\"\" border=\"0\" height=:9pt…!”
"Text": null,
"DefaultNew": false,
"DefaultReply": false
},
. . .
- Clean flag represents value of “Delete all existing email signatures in Outlook” preference.
- Signature supposed to be downloaded directly into “Signatures” folder of Outlook user profile. Each signature must be saved into a file named exactly as field
Name
value with.html
extension added.
[HttpPost, Route("api/1.0/employee/upload")]
Scope: employeectrl
Request:
{
"Items": [
{
"Email":"user1@testapi.com",
"FirstName":"John",
"LastName":"Doe",
"DisplayName":"John Doe",
"Fields": [
{
"Code":"City",
"FieldValue":"New York"
},
{
"Code":"Country",
"FieldValue":"USA"
}
]
},
{
"Email":"user2@testapi.com",
"FirstName":"Peter",
"LastName":"Johnson",
"DisplayName":"",
"Fields": [
{
"Code":"Department",
"FieldValue":"Sales"
},
{
"Code":"Facebook",
"FieldValue":""
}
]
}
]
}
Response:
{
"Items": [
"user1@testapi.com: OK",
"user2@testapi.com: OK"
]
}
[HttpPost, Route("api/1.0/employee/delete")]
Scope: employeectrl
Request:
{
"Items": [
{
"Email":"user1@testapi.com"
},
{
"Email":"user2@testapi.com"
}
]
}
Response: None
Image
Signature may contain images, like company logo, employee avatar or whatever else. Xink stores those images and performs several action to prepare Outlook application to use them properly. Field Name provides exact file name for corresponding image. Images supposed to be saved in the same directory as signatures. It is important; signature HTML refers images just by file name. Xink server is responsible for making image names unique. If in any case image names are repeated, it is guaranteed that these are references to the same image. There is no dedicated authorization scope for image access. It is enough to provide a token generated for valid client account.
[HttpPost, Route("api/1.0/image")]
Content-Type: multipart/form-data
Key: file
Value: someImg.png
Response:
{
"AccountUniqueName": "BT4",
"ImageUniqueName": "i",
"RelativeImageUrl": "https://i.xink.io/Images/Get/BT4/i.png",
"Name": "img21.png",
"DateCreated": "2023-10-10T09:18:18.21",
"MD5": "3A16278045564DAEIUB5A52B23C8B",
"SHA1": "A327FCE8098NIJN1E39D2683616DE71CBA4"
}
[HttpGet, Route("api/1.0/image/list/{offset:int=0}/{limit:int=500}")]
Scope: signaturectrl
{
"Limit": 500,
"Offset": 0,
"Items": [
{
"AccountUniqueName": "B4",
"ImageUniqueName": "i6",
"RelativeImageUrl": "https://i.xink.io/Images/Get/B4/i6.png",
"Name": "image001.png",
"DateCreated": "2014-06-18T11:59:38.843",
"MD5": "255F0174C3EOKN230283222FEE",
"SHA1": "89E5C303A9D90283NEED815735FF2C5239871"
},
{
"AccountUniqueName": "B3",
"ImageUniqueName": "i1",
"RelativeImageUrl": "https://i.xink.io/Images/Get/B3/i1.png",
"Name": "image002.png",
"DateCreated": "2014-06-18T12:19:18.76",
"MD5": "51BCBB0923NJEB223A2BA122E0",
"SHA1": "FC4907WOEINR0293712AF8BF8D9C727D15"
},
. . .
Signature
[HttpGet, Route("api/1.0/signature/list/{offset:int=0}/{limit:int=500}")]
Scope: signaturectrl
{
"Limit": 500,
"Offset": 0,
"Items": [
{
"SignatureID": "372236",
"Name": "eMailSignature",
"ContentText": "((FirstName)) ((LastName))\r\n((JobTitle))\r\n\r\n((Company))\r\n((Department))\r\nEmail: ((Email))\r\nPhone: ((DirectPhone))\r\n((WebSite))",
"ContentHTML": "<br />\r\n<font color=\"#464646\" face=\"Arial, Helvetica, Geneva, Sans-Serif\" style=\"font-size: 12px;\"><b>((FirstName)) ((LastName))</b><br />\r\n((JobTitle))</font>\r\n<table bgcolor=\"#272827\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border:none; mso-table-lspace:0pt; mso-table-. . . </font></font></font></p>\r\n\t\t\t</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td colspan=\"2\" height=\"10\"><font color=\"#464646\" face=\"Arial, Helvetica, Geneva, Sans-Serif\" style=\"font-size: 12px;\"><font color=\"#000000\" face=\"Arial, Helvetica, Geneva, Sans-Serif\" style=\"font-size: 8pt;\"> </font></font></td>\r\n\t\t</tr>\r\n\t</tbody>\r\n</table>\r\n",
"DefaultNew": false,
"DefaultReply": false,
"Available": false,
"Public": false,
"Notes": null
},
. . .
[HttpPost, Route("api/1.0/signature/")]
Name (string, required): The name of the signature.
ContentText (string, required): The text content of the signature.
ContentHTML (string, required): The HTML content of the signature.
Notes (string, optional): Optional notes or additional information about the signature.
Request:
{
"Name": "MySignature",
"ContentText": "Hello, this is my signature text.",
"ContentHTML": "<p>Hello, this is my HTML signature.</p>",
"Notes": "Optional notes about the signature"
}
Response:
{
"SignatureID": "12345"
}
[HttpPost, Route("api/1.0/signature/{id}/update")]
Scope: signaturectrl
Request:
{
"Name": "MySignature-UPDATED",
"ContentText": "Hello, this is my signature text UPDATED.",
"ContentHTML": "<p>Hello, this is my HTML signature UPDATED.</p>",
"Notes": "Optional notes about the signature UPDATED"
}
Response:
{
"SignatureID": "12345"
}
[HttpDelete, Route("api/1.0/signature/{id}")]
[HttpPost, Route("api/1.0/signature/{id}/delete")]
Signature Import
[HttpPost, Route("api/1.0/signatureimport/{id}/image")]
[HttpPost, Route("api/1.0/signatureimport/{id}")]
Rerouting
[HttpGet, Route("api/1.0/rerouting/status")]
{
"StatusCode": 0,
"Domains": [],
"IsSMTPCacheReady": true
}
[HttpPost, Route("api/1.0/rerouting/clearmap")]
[HttpPost, Route("api/1.0/rerouting/replacemap")]
Managed Accounts
[HttpGet, Route("/api/1.0/managedAccounts/list")]
Scope: managedaccountsread
{
"Items": [
{
"id": 26845,
"name": "admin1",
"email": "admin1@xink.io",
"employees": 1,
"licenses": 0,
"version": "Brand",
"using": 0,
"note": "4",
"folders": [],
"createdDate": "2019-02-22T08:45:29.36",
"endDate": null
},
{
"id": 28760,
"name": "admin2@xink.io",
"email": "admin2@xink.io",
"employees": 1,
"licenses": 0,
"version": "Brand",
"using": 0,
"note": "111221",
"folders": [
"1"
],
"createdDate": "2019-04-24T15:22:24.157",
"endDate": "2019-05-08T00:00:00"
},
{
"id": 30598,
"name": "admin3",
"email": "aadmin1@xink.io",
"employees": 1,
"licenses": 0,
"version": "Brand",
"using": 0,
"note": "2",
"folders": [
"1",
"2"
],
"createdDate": "2019-06-18T06:58:56.243",
"endDate": null
}
]
}