Xink provides REST API for external clients.
All traffic between a client application and Xink service (Xink Developer API) is transported by encrypted HTTP requests and responses.
Authentication/Authorization
We use OAuth2 to control access to Xink developer API; external clients should use ClientCredentialsGrant in particular. The client should request the access token from the Xink authorization server, then use the token in all subsequent requests to the API.
In Xink admin you 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 | |
Authorization endpoint | /OAuth/Authorize |
Token endpoint | /OAuth/Token |
Here is a C# sample
string accessToken = null; DateTime accessTokenExpiryTime; var authorizationServerUri = new Uri("https://auth.xink.io"); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic", Convert.ToBase64String( System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes( string.Join(":", "some-user-id@SRV", "theverysecretpassword")))); KeyValuePair<string, string>[] formData = new KeyValuePair<string, string>[] { new KeyValuePair<string,string>("grant_type","client_credentials"), new KeyValuePair<string, string>("scope","employeeread employeesignaturesread"), }; var content = new FormUrlEncodedContent(formData); var response = client.PostAsync(new Uri(authorizationServerUri, "/OAuth/Token"), content).Result; if (response.StatusCode == HttpStatusCode.OK) { var rdata = response.Content.ReadAsStringAsync().Result; IDictionary<string, string> data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rdata) as IDictionary<string, string>; accessToken = data["access_token"]; accessTokenExpiryTime = DateTime.UtcNow.AddSeconds(int.Parse(data["expires_in"])); } else if (response.StatusCode == HttpStatusCode.BadRequest) { var rdata = response.Content.ReadAsStringAsync().Result; IDictionary<string, string> data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rdata) as IDictionary<string, string>; } if (string.IsNullOrEmpty(accessToken)) { Console.WriteLine("Couldn't obtain token."); return; }
Please note scope names “employeeread” and “employeesignaturesread”, as these are required to get employee list and signatures.
Each operation may require some scope.
By default, the token is valid for 2 hours from the time of the authorization request. At a later point, this value might be changed to 10-15 minutes.
Finding API server
After obtaining the access token client should call the discovery service and get the API server DNS name, it is different for accounts in different data centers. Discovery service URL is https://auth.xink.io/discovery, this can be hardcoded.
The service returns a simple JSON object containing the APIRoot parameter.
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); var dcConfig = client.GetStringAsync( new Uri( authorizationServerUri, "/discovery" ) ).Result; IDictionary<string, string> dcConfigData = JsonConvert.DeserializeObject<Dictionary<string, string>>(dcConfig) as IDictionary<string, string>; var resourceServerUri = new Uri(dcConfigData["APIRoot"]);
The last variable here, resourceServerUri, contains the API root reference.
Employees
We use OAuth2 to control access to Xink APIs; external clients should use ClientCredentialsGrant in particular. The client should request the access token from the Xink authorization server, then use the token in all subsequent requests to the Xink API.
Route | api/1.0/employee/list |
Authorization scope | employeeread |
Parameters | offset: integer, default is 0. Position 0 limit: integer, default is 500. Position 1 |
Response format (sample) | { “Offset”: 0, “Limit”: 3, “Items”: [ { “Email”: “johndoe@example.com”, “FirstName”: “John”, “LastName”: “Doe”, “DisplayName”: “John Doe” }, … ] } |
Xink API URL for fetching the first three employees would be api/1.0/employee/list/0/3
The response contains Offset and Limit parameter values used in the request (or defaults if not parameters are supplied). In the array of Items, each represents one employee.
Employee list items:
- Email — employee email is a system-wide unique value used to refer employee in any calls. Cannot be empty.
- FirstName — corresponds to Xink First name field.
- LastName — corresponds to Xink Last name field.
- DisplayName — corresponds to the Xink Display Name field. Cannot be empty.
Method | POST |
Route | api/1.0/employee/invite |
Authorization scope | employeeread |
Parameters | none |
Response format(sample) | none |
Request body (sample) | { "Email": “user1@example.com” } |
Method | GET |
Route | api/1.0/employee/listprofile/{offset:int=0}/{limit:int=200} |
Authorization scope | employeefieldsread |
Parameters | offset: integer, default is 0. Position 0 limit: integer, default is 500. Position 1 |
Response format(sample) | { "Limit": 200, "Offset": 0, "Items": [ { "Email": “user1@example.com", "FirstName": "Test", "LastName": "Test", "DisplayName": "Test Test", "Fields": [ { "Code": "FirstName", "DisplayName": "First Name", "FieldValue": "Test", "IsImage": false }, … ] } |
Request body (sample) | none |
Route | api/1.0/employee/{email}/signatures |
Authorization scope | employeesignaturesread |
Parameters | email: email address of the employee |
Response format (sample) | { “Clean”: true, “SetDefault”: true, “SetReply”: true, “Signatures”: [ { “Name”: “Some Name”, “HTML”: “<p>Sincerely,<br/>John Doe</p>”, “Text”: “Sincerely,\r\nJohn Doe”, “DefaultNew”: true, “DefaultReply”: true }, … ], “Images”: [ { “Name”: “images-Get-i62-f.jpg”, “URL”: “https://app.xink.io/Images/Get/i62/i.jpg” }, … ] } |
URL for fetching John Doe’s signature would be api/1.0/employee/johndoe@example.com/signatures
The response contains the Clean flag which represents the value of “Delete all existing email signatures in Outlook” preference; SetDefault and SetReply flags indicating if the client application is supposed to assign new/reply signatures in Outlook and actual array of Signatures and Images.
Signature list items:
- Name — the name of the signature in Xink, the same value is displayed in the web application, in the signature list. Cannot be empty.
- HTML — this is the actual HTML signature with all substitutions performed, ready for use by a specific employee.
- Text — plain text version of the signature, also ready for use.
- DefaultNew — a boolean flag indicating that the signature is configured to be the default for new messages.
- DefaultReply — a boolean flag indicating that the signature is configured to be the default for forward/reply messages.
Images list items:
- Name — file name for the image
- URL — direct link to fetch the image.
The signature is supposed to be downloaded directly into the “Signatures” folder of the Outlook user profile. Each signature must be saved into a file named exactly as field Name value with the .htm extension added.
Route | api/1.0/employee/{email}/fields |
Authorization scope | employeefieldsread |
Parameters | email: email address of the employee |
Response format (sample) | { “Fields”: [ { “Code”: “Mobile”, “DisplayName”: “<p>Sincerely,<br/>John Doe</p>”, “FieldValue”: “Sincerely,\r\nJohn Doe” }, … ] } |
URL for fetching John Doe’s fields would be api/1.0/employee/johndoe@example.com/fields
The response contains an array of Fields.
Field list items:
- Code — internal name of Xink field; used by Xink to refer field data in signatures, like ((Mobile)). Mobile is the code.
- DisplayName — human-readable name (or description) of the field.
- FieldValue — the actual value of the field for the corresponding user in a moment when API calls made.
Method | GET |
Route | api/1.0/fields/list |
Authorization scope | employeefieldsread |
Parameters | none |
Response format (sample) | { "Fields": [ { "Code": "FirstName", "DisplayName": "First Name", "FieldType": 1, "DirectoryMapping": "givenName" }, { "Code": "LastName", "DisplayName": "Last Name", "FieldType": 1, … } ] } |
Method | POST |
Route | api/1.0/employee/upload |
Authorization scope | employeectrl |
Parameters | none |
Request body (sample) | { “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 format (sample) | { “Items”: [ “user1@testapi.com: OK”, “user2@testapi.com: OK” ] } |
Upload list items:
- Email — employee email. A mandatory field, a unique employee identifier in the system-wide.
- FirstName — corresponds to Xink First name field. A mandatory field for new employees. It can be left blank for already existed employees. It won’t change in this case.
- LastName — corresponds to Xink Last name field. A mandatory field for new employees. It can be left blank for already existed employees. It won’t change in this case.
- DisplayName — corresponds to the Xink Display Name field. Can be empty. In this case “FirstName LastName” will be applied.
- Fields — a list of the employee custom fields. Only the fields in the list are gonna change. The field FieldValue can be empty. The fields should be created in Xink. If the request contains the fields, which can’t be found in Xink, the employee won’t be added. The server answer contains the message with the successful employee update or error description.
Method | POST |
Route | api/1.0/employee/delete |
Authorization scope | employeectrl |
Parameters | none |
Request body (sample) | { “Items”: [ { “Email”:”user1@testapi.com” }, { “Email”:”user2@testapi.com” } ] } |
Response format (sample) | none |
Delete list items:
- Email — employee email. A mandatory field, a unique employee identifier in the system-wide.
Images
The signature may contain images, like the company logo, employee avatar or whatever else. Xink stores those images and performs several actions to prepare Outlook application to use them properly.
Field Name provides the exact file name for the corresponding image. Images supposed to be saved in the same directory as signatures. It is important; signature HTML refers to 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 a valid client account.
Example
var body = client.GetStringAsync( new Uri( resourceServerUri, "/api/1.0/employee/johndoe@example.tld/signatures" ) ).Result;