Mapping UserVoice data to your own

UserVoice API now provides a way to map your system’s data to its own. You create mappings that are called External IDs. This is useful in cases where you want to link your data to UserVoice data or if you want to maintain information in your system using data in UserVoice. For example, you might want to link suggestions or tickets to your own issue or ticketing system and let UserVoice provide easy links back to your system. We provide an API for this.

External IDs

External IDs simply mean storing another system’s IDs in UserVoice. You don’t need to make any changes to your own system’s data model. All you need is unique primary keys that UserVoice can store in the external_id array of each data record. Any data record in UserVoice can have as many data mappings as you want. These mappings can be used in various ways, especially in synchronizing data. You can choose between global and selective synchronization. For the time being, only suggestions (tickets support will be added) can be selectively synchronized by polling UserVoice, but the system will be improved to utilize service hooks in the future. Pushing data is much more efficient than polling.

Global Ticket and Suggestion Synchronization

Typically, you want to request the records that do not yet have a mapping in UserVoice. This can be achieved by calling the listing endpoints (/api/v1/suggestions or /api/v1/tickets) in the UserVoice API. Then, we just map the suggestions to our own data model. Suppose that we have the following set of records that we would like to map from one system to another.

1
2
3
4
5
6
# Request unmapped tickets in scope 'issues'
client.get("/api/v1/tickets.json?filter=without_external_id&external_scope=issues")

# Response (contents suppressed from original):
=> [{"id":93,"ticket_number":2,"subject":"Background not working","state":"open","external_ids":[]},
    {"id":94,"ticket_number":1,"subject":"Welcome!","state":"open","external_ids":[]}]

Mappings are created with identify call. Let’s map the tickets first. At this point you can create the records in your system, if you are doing a full synchronization. The identify serves as a confirmation that lets UserVoice know about the synchronized tickets.

1
2
3
4
5
6
7
8
9
10
11
# Map tickets 93 and 94 to 3 and 4 in scope 'issues'
client.put("/api/v1/tickets/identify.json, {
 "external_scope": 'issues',
 "identifications": [
     { "id": 93, "external_id": 3 },
     { "id": 94, "external_id": 4 }
 ]
})

# Response (contents suppressed from original):
=> {"identifications":{"ids":[93,94]}}

Now we have connected the two tickets in UserVoice to their corresponding issues, as shown in the diagram below.

Then, let’s query for the suggestions that haven’t been synchronized.

1
2
3
4
5
# Request unmapped suggestions in scope 'cases'
client.get("/api/v1/suggestions.json?filter=without_external_id&external_scope=cases")

# Response (contents suppressed from original):
=> [{"id":84,"state":"approved","title":"Add menubar","external_ids":[]}]

To connect the suggestion, we have to do the following kind of request:

1
2
3
4
5
6
7
8
9
10
# Map suggestion 84 to 7 in scope 'cases'
client.put("/api/v1/suggestions/identify.json, {
 "external_scope": 'cases',
 "identifications": [
     { "id": 84, "external_id": 7 }
 ]
})

# Response (contents suppressed from original):
=> {"identifications":{"ids":[84]}}

Now we have a new mapping in our diagram.

Now we have all the suggestions and tickets mapped. We can make sure by requesting the mapped tickets and requests via these two calls:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Request mapped suggestions in scope 'cases'
client.get("/api/v1/suggestions.json?filter=with_external_id&external_scope=cases")

# Response (contents suppressed from original):
=> [{"id":84, "state":"approved", "title":"Add menubar",
     "external_ids":[ {"id":148034,"external_scope":"cases","identifier":"7","url":null} ]}]


# Request mapped tickets in scope 'issues'
client.get("/api/v1/tickets.json?filter=with_external_id&external_scope=issues")

# Response (contents suppressed from original):
=> [{"id":93, "ticket_number":2, "subject":"Background not working", "state":"open",
     "external_ids":[ {"id":147133,"external_scope":"issues","identifier":"3","url":null} ]},
    {"id":94, "ticket_number":1, "subject":"Welcome!", "state":"open",
     "external_ids":[ {"id":147133,"external_scope":"issues","identifier":"4","url":null} ]}]