Transcend

The Transcend Guide to Privacy

Welcome to the Transcend. You'll find comprehensive guides and documentation to help you start working with Transcend as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started    

Connect a Server: HTTP API

Connect your own server and database to Transcend.

Connect data from any server to Transcend via our HTTP API. You can set up a webhook that will notify your server of a new Data Subject Request (DSR).

You'll need to write code that involves HTTP requests and database lookups.

Getting Started

  1. Go to your Data Map and click Add Data Silo
  2. Select Server
  3. Give your server a title (e.g. "User Accounts")
  4. Click Connect
  5. Get your API key and store it securely

Fulfill an access request (DSAR)

When a DSAR is received, Transcend automatically compiles data across your data silos. For data you control in your databases, we send your server a webhook notification, and your server then POSTs data to our API.

When a DSAR is received Transcend will send your server some identifying info (e.g. email) about the requestor via webhooks. After receiving and verifying the webhook, Your server should look up any matching records, or "profiles", and send them to Transcend.

POST https://api.transcend.io/v0/upload

request_id

String

This is the data subject request ID, which is retrieved from the webhook

profiles

Array

A list of JSON objects with keys profile_id and profile_data. Your server may find one profile for the data subject, several profiles for the data subject, or none at all. Therefore, profiles is an array that can be of any length.

profiles[i].profile_id

String

A unique identifier for this profile

profiles[i].profile_data

Object

An arbitrary JSON object with the data payload. The keys of the JSON should match the datapoint keys you defined for this data silo.

status

String

The status to set this data silo to. One of pending or ready. If you are uploading with multiple requests, you should set this to pending.

const request = require('request');

const myDataUpload = getUserData();

request.post('https://api.transcend.io/v0/upload', {
  headers: {
    Authorization: ''
  },
  body: {
    request_id: '5e34c589-761c-22b8-85fb-7d1504cf7b8c', // Retrieved from the webhook,
    profiles: [{
      profile_id: 'ben.farrell',
      profile_data: {
        resume: <Buffer >,
        score: 3.8,
        interests: 'Privacy Tech'
      }
    }]
  },
  json: true
});
import json
import urllib2

data = {
    'request_id': '5e34c589-761c-22b8-85fb-7d1504cf7b8c',
    'profiles': [{
      'profile_id': 'ben.farrell',
      'profile_data': {
        'resume': <Buffer >,
        'score': 3.8,
        'interests': 'Privacy Tech'
      }
    }]
}

req = urllib2.Request('https://api.transcend.io/v0/upload')
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', '')

response = urllib2.urlopen(req, json.dumps(data))
curl -X POST "https://api.transcend.io/v0/upload" \
  -H "Authorization: " \
  -H "Content-Type: application/json" \
  -d "{
    request_id: '5e34c589-761c-22b8-85fb-7d1504cf7b8c',
    profiles: [{
      profile_id: 'ben.farrell',
      profile_data: {
        resume: <Buffer >,
        score: 3.8,
        interests: 'Privacy Tech'
      }
    }]
  }"

Be sure to authenticate your request

Include your data silo's API key. This is separate from your organization's API key. Read more about authenticating with the Transcend API.

Fulfill an Erasure Request (DSER)

When a DSER is received, Transcend sends your server a webhook notification, and your server then deletes data from your own databases. When your server is done, it should notify Transcend about which profiles were erased.

POST https://api.transcend.io/v0/erasure-response
Parameter
Type
Description

request_id

String

This is the data subject request ID, which is retrieved from the webhook.

profiles

Array

A list of JSON objects with key profile_id

profiles[i].profile_id

String

A unique identifier for this profile

const request = require('request');

request.post('https://api.transcend.io/v0/erasure-response', {
  headers: {
    Authorization: ''
  },
  body: {
    request_id: '5e34c589-761c-22b8-85fb-7d1504cf7b8c', // Retrieved from the webhook,
    profiles: [{ // Profiles without the data
      profile_id: 'ben.farrell',
    }]
  },
  json: true
});
import json
import urllib2

data = {
    'request_id': '5e34c589-761c-22b8-85fb-7d1504cf7b8c',
    'profiles': [{
      'profile_id': 'ben.farrell',
    }]
}

req = urllib2.Request('https://api.transcend.io/v0/erasure-response')
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', '')

response = urllib2.urlopen(req, json.dumps(data))
curl -X POST "https://api.transcend.io/v0/erasure-response" \
  -H "Authorization: " \
  -H "Content-Type: application/json" \
  -d "{
    request_id: '5e34c589-761c-22b8-85fb-7d1504cf7b8c',
    profiles: [{
      profile_id: 'ben.farrell',
    }],
  }"

Webhooks

In order for your server to know there's a new data subject request, it should be notified by receiving a webhook. You can set up as many webhooks as you like by adding additional Server data silos. This is especially useful when you have many microservices.

Receiving a webhook

Receive a webhook by creating an HTTP endpoint that receives a POST request. Then, set up the webhook by going to Data Map, selecting your Server data silo, clicking Connection Settings, and entering your endpoint URL.

The webhook will be a POST request with some data in the body:

{
  "type": "access",
  "identifiers": {
    "first_name": "Ben",
    "last_name": "Farrell",
    "email": "benfarrell@gmail.com"
  },
  "is_verified": true,
  "request_id": "123124-123-aa18d",
  "status": "compiling",
  "is_test": false,
  "organization_id": ""
}
Attribute
Type
Description

type

String

The type of DSR. One of access, erasure, portability, rectification, objection, or restriction

identifiers

Object

An object containing the form inputs on the DSR form on your Privacy Center. Often it includes an email address and name, but can include any custom inputs you specify for your DSR form.

is_verified

Boolean

Whether the data subject has successfully verified their identity yet

request_id

String

The ID of the incoming DSR

status

Boolean

The current stage of the request

is_test

Boolean

Whether this is a test request or not (if not, it's a real DSR coming from the Privacy Center)

organization_id

String

The unique ID for your organization

Security Tip: always verify the webhook's signature

Make sure the webhook notification came from Transcend by verifying that the token in the signature header matches. If it doesn't match, you should ignore the request. Read more.

Verify the webhook signature

Transcend signs webhooks with JSON Web Signatures (JWS). You should verify this signature to be certain that requests are originating from Transcend.

You'll find the JSON Web Signature in the Signature header of the incoming request. It'll look like this:

Signature: eyJhbGciOiJIUzI1NiJ9.eyJrIjoiYXNkZiIsImIiOiI1MzE0MiIsImMiOiIxMjNhc2RmOCIsImQiOnsiYyI6ImFzZGYiLCJkIjp7ImsiOiJhc2RmIn19fQ.VZrjXoiD_044VXQvPiO_Tk_hNbaIdnSyjfTwsA2oL8I
or
Signature: part1.part2.signature

Option 1: Use a JWS library

// verify with node-jws (the JWT library, jsonwebtoken, would work too)
const jws = require('jws');
const isVerified = jws.verify(req.headers.signature, 'HS256', mydatasiloapikey);

JWT libraries also work!

JWS (JSON Web Signatures) is a component of the JWT (JSON Web Token) standard. JWT is widespread, so there are more libraries for the full JWT system than JWS alone. All JWT and JWS libraries will have the verify function.

Option 2: Verify it manually

The JWS is in the signature header. It's comprised of three concatenated strings with dots between them: "part1.part2.part3".

To compute the signature:

  1. Create an HMAC-SHA-256 using your data silo's API Key
  2. Hash the substring containing the first two parts "part1.part2"
  3. Base64 encode it
  4. This is your computed signature. Compare it to the given signature (part3). Using == is not advised. Instead, use a constant-time comparison algorithm (example)
// Verify the webhook manually
const securedInput = req.headers.signature.split('.', 2).join('.');
const signature = req.headers.signature.split('.')[2];

const hmac = crypto.createHmac('sha256', mydatasiloapikey);
let computedSig = (hmac.update(securedInput), hmac.digest('base64'));
computedSig = computedSig.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // from base64

const isVerified = crypto.timingSafeEqual(Buffer.from(computedSig), Buffer.from(signature));

Note: you'll also need to replace some Base64 characters:

  • = with (space)
  • + with -
  • / with _