Python Client

The RedShelf Python client is a reference client for building network requests to the API. The current version available is version 1.0 which provides basic network request functionality.

Requirements

The client library requires the following libraries:

Install:

pip install requests pycrypto simplejson

Installation

Clone the client library from github for installation:

git clone https://github.com/VirdocsSoftware/redshelf-api-client.git

Everything you need is contained inside the client.py module. You may deploy the client.py using your own proceseses or continue to use github for deployment.

Basic Usage

Import and instance the client object:

from client import ClientV1
c = ClientV1()

Supply your authentication information (for more details see the authentication section):

c.set_user('0d72df20f23558620646fb3ea030f5')
c.load_key('/path/to/private/key.key')

The client library provides several convenience functions that perform the task of formatting and executing network calls to the API. The next sections cover the available commands.

It is recommended that keyword arguments are always used when possible to minimize any compatibility issues in the future.

Do:

function(arg1=value1, arg2=value2, ...)

Don’t:

function(value1, value2, ...)

Error Handling

All responses from the API are converted into Python dict() or {} before being returned from the function. The HTTP response code will always be in the root level of the dict as the key code. Typically any error will also include a message key indicating the nature of the problem.

In the event of a response from the API which cannot be deserialized the client library will return a generic dict which contains the code key indicating the HTTP response code and the text key with any text returned.

General Commands

General commands are not versioned and are not considered mission critical. Specifications for these endpoints could change at any time.
client.Client.index()

GET /

Return the API index which includes general information about the current status of the service.

Example output:

{
 'api': {
        'admin': 'terry@virdocs.com',
        'available_versions': [
        {
            'version': 'v1',
            'version_date': '2014-03-06',
            'version_help': '/v1/describe/',
            'version_major': 1,
            'version_string': '1.0.0',
            'version_url': '/v1/'
        }],
        'current_version': 'v1',
        'host': 'api.redshelf.com'
    },
     'code': 200,
     'success': True,
     'test_mode': False
 }
client.Client.profile()

GET /profile/

Return information about your account including access constraints.

Example output:

{
 'code': 200,
 'profile': {
    'address_1': '747 N. LaSalle St',
    'address_2': 'Suite 220',
    'city': 'Chicago',
    'country': 'US',
    'nickname': 'RedShelf',
    'state': 'IL',
    'zip': '60654'
 },
 'scopes': ["book_metadata", "invite_user"],
 'success': True,
 'test_mode': False
}

Book Commands

client.ClientV1.book(hash_id=<string>)
client.ClientV1.book(isbn=<string>)
client.ClientV1.book(sku=<string>)

GET /v1/book/<hash>/

GET /v1/book/isbn/<isbn>/

GET /v1/book/sku/<sku>/

Returns information about a specific book when given either a hash_id OR isbn. Information returned may include metadata, pricing, distribution, and white label configuration.

Example:

c.book(hash_id='ccc5a093f5ee30ec749e4909840f0f7b794066bb')

{
  'code': 200,
  'success': True,
  'test_mode': False,
  'book': {
    'id': 876,
    'hash_id': 'ccc5a093f5ee30ec749e4909840f0f7b794066bb',
    'title': 'Test Combo Book',
    'subtitle': 'This is a subtitle',
    'description': 'This is a book description.',
    'author': 'Author name or sequence of names',
    'language': 'English',
    'num_pages': 0,
    'publish_year': None,
    'sample_url': '/book/sample/5/',
    'identifiers': {
        'eisbn10': '1234567890',
        'eisbn13': '1234567890123',
        'hash_id': 'ccc5a093f5ee30ec749e4909840f0f7b794066bb',
        'id': 876,
        'isbn10': '',
        'isbn13': '',
        'parent_isbn': '',
        'sku': ''
    },
    'drm': {
        'copy_percent': None,
        'offline_percent': None,
        'offline_range': '',
        'print_allowance_percent': 0.250,
        'print_allowance_range': '',
        'sample_page_end': 0,
        'sample_percent': None
    },
    'files': {
        'cover_image': {
            'filename': 'default_book_cover.jpg',
            'url': '//content.redshelf.com/site_media/media/cover_image/default_book_cover.jpg'
        },
        'thumbnail': None
    },
    'digital_pricing': [
        {
           'calculated_expiration_date': '2014-05-29',
           'currency': 'USD',
           'days_until_expiration': '78',
           'deactivation_date': '2014-05-29T05:00:00Z',
           'description': 'Rent eBook (until 2014-05-29)',
           'description_limit': '2014-05-29 05:00:00+00:00',
           'id': 932,
           'is_limited': True,
           'limit_days': 0,
           'other_pricing': 'EXPIRATION',
           'price': '2.99'
        },
        {
           'calculated_expiration_date': '2014-04-12',
           'currency': 'USD',
           'days_until_expiration': '31',
           'deactivation_date': None,
           'description': 'Rent eBook (30 days)',
           'description_limit': '30 days',
           'id': 927,
           'is_limited': True,
           'limit_days': 30,
           'other_pricing': 'LIMITED',
           'price': '5.99'
        },
        {
           'currency': 'USD',
           'deactivation_date': None,
           'description': 'Buy eBook (lifetime)',
           'description_limit': 'lifetime',
           'id': 908,
           'is_limited': False,
           'limit_days': 0,
           'other_pricing': None,
           'price': '20.00'
        }
    ],
    'publisher': {
        'title': 'Some Publisher Title',
        'description': 'Publisher Description',
        'is_active': True
    },
    'status': {
        'is_active': True,
        'is_html': True,
        'is_processed': True,
        'is_public': True,
        'is_published': True,
        'is_queued': True,
        'processed_date': '2014-03-12T16:00:22Z',
        'queued_date': '2014-03-12T16:06:01Z'
    }
  }
}

Note: Additional data may be returned for each data point depending on your account permissions.

See also

For more information on the data structures used throughout the API please refer to the Data Structures section.

client.ClientV1.book_search(title=<string>)
client.ClientV1.book_search(author=<string>)

POST /v1/book/search/

Search for books matching the specified title or list of ISBN numbers.

Example:

c.book_search(isbn=['1234567890123'])

{
 'code': 200,
 'count': 1,
 'results': [
    '<book object>'
 ],
 'success': True,
 'test_mode': False
}

Note: See book() function above for format of book object.

client.ClientV1.book_index(limit=<int>, offset=<int>)

POST /v1/book/index/

Obtain a list of books controlled by the current account.

Example:

c.book_index()

{
 'code': 200,
 'count': 1,
 'results': [
    '<book object>'
 ],
 'success': True,
 'test_mode': False
}

Note: See book() function above for format of book object.

client.ClientV1.book_viewer(username=<string>, hash_id=<string>)

POST /v1/book/viewer/

Get a URL to the reader for a given user and book.

Example:

c.book_viewer(username='0d72df20f23558620646fb3ea030f5', hash_id='ccc5a093f5ee30ec749e4909840f0f7b794066bb')

{
    'code': 200,
    'success': True,
    'test_mode': True,
    'viewer_url': 'https://platform.virdocs.com/viewer/MjQ5MzI6dGVycnlAdnlyZG9jcy5jb21SUzoxNDAwMTAwMjI3OmYyNTAyOTcwZTZhY2E4N2ZjNDA5MDNjODllZmZlYjll'
}
client.ClientV1.book_process(hash_id=<string>, force=<boolean>, urgent=<boolean>)

POST /v1/book/<hash_id>/process/

Enqueue a book for processing.

Example:

c.book_process(hash_id='ccc5a093f5ee30ec749e4909840f0f7b794066bb')

{
    'code': 200,
    'message': 'Book queued.',
    'success': True,
    'test_mode': False
}

User Commands

client.ClientV1.invite_user(email=<string>, first_name=<string>, last_name=<string>)
client.ClientV1.invite_user(email=<string>, first_name=<string>, last_name=<string>, label=<string>)

POST /v1/user/invite/

Create a new user account and send an invitation email to that user with a generated password.

Example:

c.invite_user(email='example@user.com', first_name='Example', last_name='User')

{
 'code': 200,
 'email': 'example@user.com',
 'message': 'Invitation sent.',
 'success': True,
 'test_mode': False,
 'username': '0d72df20f23558620646fb3ea030f5'
}

If the user already exists. you will receive the following response containing the existing user’s username hash.

400 BAD REQUEST:

{'code': 400
 'message': 'User with email address already exists',
 'value': example@user.com,
 'conflict': True,
 'field': 'email',
 'username': '0d72df20f23558620646fb3ea030f5'
}

If you are assigning this user to a white label under your control you may provide the label field with the subdomain for the associated white label.

Important: Store the username field returned from the API such that it’s associated with your own internal records for the user. While lookups using the email address are possible, the username is the primary identification used throughout the API.

client.ClientV1.create_user(email=<string>, first_name=<string>, last_name=<string>)
client.ClientV1.create_user(email=<string>, first_name=<string>, last_name=<string>, passwd=<string>, passwd_confirm=<string>, label=<string>)

POST /v1/user/

Create a new user account silently. Password field can be omitted to generate a random password.

Example:

c.create_user(email='example@user.com', first_name='Example', last_name='User')

{
 'code': 200,
 'email': 'example@user.com',
 'passwd': 'Y9ivTu%6uH',
 'message': 'User created.',
 'success': True,
 'test_mode': False,
 'username': '0d72df20f23558620646fb3ea030f5'
}

If you are assigning this user to a white label under your control you may provide the label field with the subdomain for the associated white label.

client.ClientV1.user(username=<string>)
client.ClientV1.user(email=<string>)

GET /v1/user/<username>/

GET /v1/user/email/<email>/

Get information for a user.

Example:

c.user(username='0d72df20f23558620646fb3ea030f5')

{
    'code': 200,
    'test_mode': True,
    'user': {
        'username': '0d72df20f23558620646fb3ea030f5',
        'first_name': 'Example',
        'last_name': 'User',
        'email': 'example@user.com',
        'status': {
            'is_active': True,
            'verified': True,
            'last_login': '2014-04-09T21:14:13.207Z',
            'over_18': True,
            'date_joined': '2013-07-29T22:44:26Z'
        },
        'profile': {
            'address_1': '747 N. LaSalle St',
            'address_2': 'Suite 220',
            'city': 'Chicago',
            'zip': '60654',
            'country': 'US',
            'state': 'IL',
            'nickname': 'RedShelf'
        },
        'owner': {
            'username': 'd37f9a16767bb553cd69910567a80f',
            'full_name': 'Administrator'
        },
        'permissions': {
            'html_posting': True,
            'bdp_posting': False,
            'can_post': True,
        }
    },
    'success': True
}
client.ClientV1.user_orders(username=<string>)

GET /v1/user/<username>/orders/

Get a list of completed orders for a user.

Example:

c.user_orders(username='0d72df20f23558620646fb3ea030f5')

{
    'code': 200,
    'count': 2,
    'results': [
        {
            'id': 5,
            'is_complete': True,
            'item_count': 1,
            'items': [
                {
                    'id': 72,
                    'order': {
                        'id': 5
                    },
                    'book': {
                        'hash_id': '6cff8f25c311b238b70dcec66d225f29bb128776',
                        'title': 'Test Title',
                        'sample_url': '/book/sample/5/'
                    },
                    'expiration_date': '2014-04-15T05:00:00Z',
                    'is_active': True,
                    'is_digital': True,
                    'is_print': False,
                    'is_refunded': False,
                    'refund_available': False,
                    'purchase_date': '2014-04-11T16:33:06.762Z',
                    'purchase_price': '0.00',
                    'quantity': 1,
                    'user': {
                        'full_name': 'Example User',
                        'username': '0d72df20f23558620646fb3ea030f5'
                    }
                }
            ],
            'totals': {
                'discount': '0.00',
                'processing_fee': '0.00',
                'shipping_fee': '0.00',
                'subtotal': '0.00',
                'taxes': '0.00',
                'total': '0.00'
            },
            'username': '0d72df20f23558620646fb3ea030f5'
        }
    ],
    'success': True,
    'test_mode': False
}

Order Commands

client.ClientV1.create_order(username=<string>, digital_pricing=<list>, billing_address=<dict>)
client.ClientV1.create_order(username=<string>, digital_pricing=<list>, billing_address=<dict>, label=<string>)

POST /v1/order/external/

Provides a method for reporting orders processed outside of the RedShelf system. This includes orders where the integration partner is using their own checkout and fund collection processes.

Example:

username = '0d72df20f23558620646fb3ea030f5'
address = format_address(full_name='Example User', postal_code='60527', country='US', order_type='')

c.create_order(username=username, digital_pricing=[123], billing_address=address)

{
    'code': 200,
    'message': 'Order created.',
    'success': True,
    'test_mode': False,
    'order': {
        'id': 5,
        'username': u'0d72df20f23558620646fb3ea030f5',
        'is_complete': True,
        'item_count': 1,
        'items': [
            {
                'order': {
                    'id': 5
                },
                'book': {
                    'hash_id': 'ccc5a093f5ee30ec749e4909840f0f7b794066bb',
                    'title': 'Test Combo Book',
                    'sample_url': '/book/sample/5/'
                },
                'user': {
                    'full_name': 'Example User',
                    'username': '0d72df20f23558620646fb3ea030f5'
                },
                'expiration_date': None,
                'id': 98,
                'is_active': True,
                'is_digital': False,
                'is_print': True,
                'is_refunded': False,
                'purchase_date': '2014-02-28T21:30:21.335Z',
                'purchase_price': '12.99',
                'quantity': 1
            }
        ],
        'totals': {
            'subtotal': 12.99,
            'shipping': 5.99,
            'taxes': 0.00,
            'total': 19.80
        }
    },
}

username: The hashed user identifier (required)

digital_pricing: A list of integer IDs for the items being sold; the ID is found in the id field of the pricing dict for each book. (required)

billing_address: The address the address that will be associated with the purchasing order. format: {‘full_name’: ‘Example User’, postal_code=‘60527’, country=’US’} (required)

shipping_address: The shipping address that will be associated with the order if the order is Print On Demand books. (optional)

order_type: The payment plan to be executed with the order. (required)

org: The organization through that is supply the order. (required)

label: the sub-domain of the label associated with the order. (optional)

send_email: A boolean field that indicates whether an email reporting transaction details will be sent to Members of the optionally supplied label. default value: True (optional)

order_number: a unique identifier that will be supplied with the order. (optional and idempotent, max length: 20 characters)

client.ClientV1.order_refund(id=<integer>, type=<string>)
client.ClientV1.order_refund(items=<list>, type=<string>)

POST /v1/order/refund/

Provides a method for reporting refunds processed outside of the RedShelf system. This includes orders where the integration partner is using their own checkout and fund collection processes.

The ID is the order ID returned when an order is created. Optionally a list of item IDs from the order information can be supplied for partial refunds.

Example:

c.order_refund(id=1)

{
    'code': 200,
    'test_mode': True,
    'refunds': [
        {
            'purchase_id': 64,
            'success': True
        }
    ],
    'success': True
}
client.ClientV1.order_free(username=<string>, hash_id=<string>)
client.ClientV1.order_free(username=<string>, hash_id=<string>, expiration_date=<string>, label=<string>)

POST /v1/order/free/

Create an order for free access to a title. Requires username and book hash_id. Optional field expiration_date should be an ISO formatted datetime string, ex: 2014-01-01 00:00:00.

Example:

c.order_free(username='0d72df20f23558620646fb3ea030f5', hash_id='6cff8f25c311b238b70dcec66d225f29bb128776')

{
    'message': 'Order created.',
    'code': 200,
    'test_mode': False,
    'success': True
}

Code Commands

client.ClientV1.code_generation(hash_id=<string>, org=<string>, count=<int>)
client.ClientV1.code_generation(hash_id=<string>, org=<string>, count=<int>, limit_days=<int>)
client.ClientV1.code_generation(hash_id=<string>, org=<string>, count=<int>, expiration_date=<string>)
client.ClientV1.code_generation(hash_id=<string>, org=<string>, count=<int>, samples=<boolean>)
client.ClientV1.code_generation(hash_id=<string>, org=<string>, count=<int>, label=<string>)

POST /v1/codes/generate/

Generate codes for end-user book access. The limit_days field controls the number of days from code redemption the user’s access will be available for. The expiration_date field sets a hard date after which user access is disabled.

Example:

c.code_generation(hash_id='efb237d3edd664c4f81f87d9b27abfa37228e9b3', org='MYCOMPANY', count=10, limit_days=180)

{
    'code': 200,
    'success': True,
    'test_mode': False
    'codes': [
        {
            'code': 'XXXXXXXXXXXXXXXX',
            'deactivation_date': None,
            'limit_days': 180
        },
        {
            'code': 'XXXXXXXXXXXXXXXX',
            'deactivation_date': None,
            'limit_days': 180
        },
        ...
   ]
}
client.ClientV1.code_summary()

GET /v1/codes/summary/

Get summary information for the number of codes generated and redeemed on an account.

Example:

c.code_summary()

{
    'code': 200,
    'codes_generated': 69,
    'codes_redeemed': 7,
    'redemption_percentage': 0.1014,
    'success': True,
    'test_mode': False
}

Import Commands

client.ClientV1.import_search(title=<string>)
client.ClientV1.import_search(author=<string>)()

GET /v1/import/search/

Search the list of imported titles. These titles are not available for sale currently but are incoming titles.

Example:

c.import_search(title='college success')

{
    'code': 200,
    'count': 1,
    'limit': 100,
    'offset': 0,
    'results': [
        {
            'title': 'Cornerstones for Career College Success, 3/e',
            'author': 'Patricia G. Moody, Robert M. Sherfield',
            'publisher': 'Prentice Hall',
            'edition_number': 3,
            'identifiers': {
                'book_id': 0,
                'eisbn10': '032184954X',
                'eisbn13': '9780321849540',
                'isbn10': '0132789353',
                'isbn13': '9780132789356'
            },
            'status': {
                'deleted': false,
                'file_received': false,
                'imported': false
            }
        },
    ],
    'success': true,
    'test_mode': false
}

Misc. Commands

client.ClientV1.describe()

GET /v1/describe/

Returns a representation of all available endpoints including field definitions.

Support Functions

client.format_address(first_name, last_name, company_name, line_1, line_2, city, state, postal_code, country)
client.format_address(full_name, company_name, line_1, line_2, city, state, postal_code, country)

Returns a dict formatted for use in sending addresses to the API. For an example see the order commands.