GenNotes API

The GenNotes API allows other services to interact with GenNotes to retrieve, add, and edit the data in the GenNotes database.

1. Retrieving data

Data can be retrieving using GET commands. No GenNotes account is needed to retrieve data. Data is returned in JSON format.

Note: The API is intended for programmatic use, e.g. using the Python 'requests' module. Navigating to these pages in the browser will render the response within a page layout. To see raw JSON that is returned, you can access JSON content directly within the browser by adding '.json' to the end, e.g.

1.1 Get individual variant data

An individual variant's data can be retrieved based on build 37 coordinates, or based on the GenNotes variant ID.

Example GET commands:

1.2 Get multiple variant data

Multiple variants can be retrieved at once by calling `/api/variant/` with the `variant_list` parameter set to a JSON-formatted list of variant IDs. (Results are only returned for valid variants; identifiers not matching variants in GenNotes will silently fail.)

Example GET command:

1.3 Get individual relation data

An individual relations's data can be retrieved based on the GenNotes relation ID. Currently querying multiple relations is not supported.

Example GET commands:

2. Adding and editing data

2.1 Authentication

2.1.1 Using your own credentials

Note: This is not the anticipated method of submitting edits.

The anticipated method for submitting changes is through apps that you authorize to submit to GenNotes on your behalf (e.g. Genevieve). However it is possible to submit directly using your account credentials, using HTTP Basic Authorization.

For example, using fake IDs and the Python requests module for a PATCH edit:

requests.patch(
    'http://localhost:8000/api/relation/123/',
    data=json.dumps({
        'tags': {'example-tag': 'example-value'},
        'commit-comment': 'Adding an example tag using PATCH and HTTP Basic Authorization.'}),
    headers={'Content-type': 'application/json'},
    auth=('username', 'password'))

2.1.2 Using behalf of another user, via OAuth2

A GenNotes account is needed to register an app that will submit edits on behalf of other GenNotes users. Although you could use any account for this purpose, you may want to create a separate "non-personal" account for your OAuth2 app. You may want to do this if, for example, you're setting up a Genevieve client.

Note: if you work with multiple accounts, be careful to check which account you're logged in to! When you're logged in, your username is displayed at the top left.

Once you have an account, you can register a client application here.

Example process for getting authorization and access tokens:

  1. Send user to: /oauth2-app/authorize?client_id=[client-id-here]&response_type=code
  2. Your default redirect_uri will receive the grant code, e.g.: yourdomain.com/path/to/redirect_uri?code=[grant-code]
  3. Exchange that grant code for a token immediately. For example, using Python with the requests module:
    • Set this up with your client_id and client_secret: client_auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
    • Set the code: code = [the grant-code you just received]
    • Set redirect_uri (required by our framework...): redirect_uri = [a redirect uri you registered]
    • Set the GenNotes receiving uri: token_uri = 'http://gennotes.herokuapp.com/oauth2-app/token/'
    • POST to this:
      response_token = requests.post(token_uri, data={
          'grant_type': 'authorization_code',
          'code': code, 'redirect_uri': redirect_uri}, auth=client_auth)
    • The response should contain an access token, e.g.:
      {'access_token': '1hu94IRBX3da0euOiX0u3E9h',
       'token_type': 'Bearer',
       'expires_in': 36000,
       'refresh_token': 'WSuwoeBO0e9JFHqY7TnpDi7jUjgAex',
       'scope': 'commit-edit'}
    • To use the refresh token to get new tokens:
      refresh_token = response_token.json()['refresh_token']
      response_refresh = requests.post(token_uri, data={
          'grant_type': 'refresh_token',
          'refresh_token': refresh_token}, auth=client_auth)

Once a user has authorized your client app to make edits on their behalf, you can use a valid access token to submit edits through the API.

For example, using fake IDs and the Python requests module for a PATCH edit:

requests.patch(
    'http://localhost:8000/api/relation/123/',
    data=json.dumps({
        'tags': {'example-tag-key': 'example-tag-value'},
        'commit-comment': 'Adding an example tag using PATCH and OAuth2 authorization.'}),
    headers={'Content-type': 'application/json',
             'Authorization': 'Bearer {}'.format(access_token)})

2.2 PATCH: Partial edit

When you submit an edit using PATCH, the tag keys you include will be created or replaced with the values you submit. Submitting edits using PATCH is preferred, as there is less danger of accidentally overwriting information.

For a Variant, a PATCH specifies the following parameters: 'edited_version', 'tags', and 'commit-comment' (optional).
For a Relation, a PATCH specifies the following parameters: 'edited_version', 'tags', 'variant' (optional), and commit-comment (optional).

Returned: In response, you receive a copy of the updated data for the object. Unfortunately, the "current_version" will be "Unknown". Due to how edit versioning is being performed, we're unable to return the version ID for the updated object in this response. A separate GET will need to be performed to discover this.

Example using fake IDs, the Python requests module and OAuth2 access token authorization:

requests.patch(
    'http://localhost:8000/api/relation/123/',
    data=json.dumps({
        'commit-comment': 'Adding an example tag using PATCH and OAuth2 authorization.'}),
        'edited_version': 456,
        'tags': {'example-tag': 'example-value'},
    headers={'Content-type': 'application/json',
             'Authorization': 'Bearer {}'.format(access_token)})

2.3 PUT: Whole edit

When you submit an edit using PUT, the object becomes redefined with the data you include. Submitting edits using PUT could be dangerous: by omitting existing data, you delete it. On the other hand, a PUT edit is the only way to delete existing tags.

For a Variant, a PUT specifies the following parameters: 'edited_version', 'tags', and 'commit-comment' (optional).
For a Relation, a PUT specifies the following parameters: 'edited_version', 'tags', 'variant', and commit-comment (optional).

Returned: In response, you receive a copy of the updated data for the object. Unfortunately, the "current_version" will be "Unknown". Due to how edit versioning is being performed, we're unable to return the version ID for the updated object in this response. A separate GET will need to be performed to discover this.

Example using fake IDs, the Python requests module and OAuth2 access token authorization:

requests.put(
    'http://localhost:8000/api/relation/123/',
    data=json.dumps({
        'commit-comment': 'Updating a Relation using PUT and OAuth2 authorization.'
        'edited_version': 456,
        'tags': {
            'example-tag': 'A new example tag, with example value here.',
            'genevieve:notes': 'Some updated notes might also be in here... or maybe we're retaining the original.',
            'genevieve:inheritance': 'recessive',
            'genevieve:trait-name': 'Hemochromatosis',
            'genevieve:clinvar-rcva-list': '["RCV000000028"]',
            'type': 'genevieve',
        },
        'variant": "http://testserver/api/variant/789/',
    }),
    headers={'Content-type': 'application/json',
             'Authorization': 'Bearer {}'.format(access_token)})

2.4 POST: New object

The POST method is used to create a new object. This method is only allowed for Relations. (Currently, new Variants cannot be added to the database via the API.)

For a Relation, a POST specifies the following parameters: 'edited_version', 'tags', 'variant', and commit-comment (optional).

Returned: In response, you receive a copy of the data for the new object, including its ID. Unfortunately, the "current_version" will be "Unknown". Due to how edit versioning is being performed, we're unable to return the version ID for the updated object in this response. A separate GET will need to be performed to discover this.

Example using fake IDs, the Python requests module and OAuth2 access token authorization:

requests.post(
    'http://localhost:8000/api/relation/',
    data=json.dumps({
        'commit-comment': 'Creating a Relation using POST and OAuth2 authorization.'
        'tags': {
            'genevieve:notes': 'Initial notes here.',
            'genevieve:trait-name': 'Hemochromatosis',
            'type': 'genevieve',
        },
        'variant": "http://testserver/api/variant/789/',
    }),
    headers={'Content-type': 'application/json',
             'Authorization': 'Bearer {}'.format(access_token)})

2.4 DELETE: Destroy object

The DELETE method is used to destroy an object. This method is only allowed for Relations. (Currently, Variants cannot be removed from the database via the API.)

For a Relation, a DELETE specifies the following parameters: 'edited_version', commit-comment (optional).

Returned: A 204 status is sent in response to a successful DELETE API call.

Example using fake IDs, the Python requests module and OAuth2 access token authorization:

requests.delete(
    'http://localhost:8000/api/relation/123/',
    data=json.dumps({
        'commit-comment': 'Removing a Relation using DELETE and OAuth2 authorization.',
        'edited_version': 456,
    }),
    headers={'Content-type': 'application/json',
             'Authorization': 'Bearer {}'.format(access_token)})