BulkSMS API Library Implementors Guide

This guide is intended for people wishing to implement the BulkSMS API client library in an unsupported language. If you simply want to use an existing client implementation of the BulkSMS API then you should read the BulkSMS API User Guide for your choice of language.

Client Library Structure

The library structure is designed to be simple to use for the typical use cases of the majority of BulkSMS users. The structure is the same, or as close as possible within language limitations, regardless of the language in use. This includes the names used for methods and the structure of arguments and returned values. While this may not be idiomatic in any given language, it helps maintain consistency in documentation and assists in support handling.

The exposed end-user API is expected to change and grow over time, adding useful features and fixing issues discovered through actual use. Equally, the underlying HTTP based service against which the client library operates is expected to change to meet these demands. It is expected that a given version of any implementation will operate against a fixed version of the underlying HTTP service. Changes to the HTTP service which can be made without breaking the library implementations may occur within a given revision of the service, otherwise a new version of the service will need to be published. Consequently, a new version of the library implementation will need to be published to take advantage of any changes to the HTTP service.

The expectation is that the vast majority of the library implementations will be object orientated and as such we’ve structured certain types to maintain consistency between implementations. These are detailed below in pseudo-code which should be understandable by most developers.

Package Naming

Where the language has packaging conventions to allow separation of different modules, the preference is to put the library in a package called “BulkSMS.SMS”, where the period is a notional hierarchical symbol. Examples of existing package naming include

Language Package Comment
Perl BulkSMS::SMS Perl has notional hierarchy with “::”
PHP BulkSMS_SMS PHP has no packaging but convention using “_”
Java com.bulksms.sms Java has strong packaging conventions

HTTP Authentication

The HTTP service makes use of HTTP Basic auth, which can operate in a challenge-response mode. In such a case an unauthenticated request would receive a 401 response from the server. The expectation would then be that the client makes a further request but now including the authentication header. This is clearly wasteful of resources. A well behaved library implementation will ensure that a suitably created authentication header is included in all requests, preemptively, without expecting or requiring a 401 response.

HTTP Request Identification

When making HTTP requests you should identify the library implementation in the User-Agent header. The preferred format is BulkSMS.com/<language>lib-<version>, where the perl library, at version 1.0.2 would identify itself as BulkSMS.com/perllib-1.0.2.

Optional JSON Request Properties

There are many properties of the JSON request entities which are optional. An example would be the “from” flag of a MessageSubmission. Where a user has not explicitly provided a value for an optional request property, you MUST NOT provide a value in the request. Many of these optional properties have user specific settings which the HTTP service will use when no value has been provided. Overriding this functionality of the HTTP service would result in differences in operation between library implementations.

Handling Errors

Errors, as presented to the end-user, are handled through exceptions. The aim is to enforce correct error handling in client code while making it easy to write understandable code for the normal, non-error cases.

Types

Ternary

enum Ternary { YES, NO, UNSPECIFIED }

StatusReportingMode

enum StatusReportingMode { ALL, ERRORS, NONE, UNSPECIFIED }

MessageBodyEncoding

enum MessageBodyEncoding { TEXT, UNICODE, BINARY }

ProtocolId

enum ProtocolId {
    IMPLICIT,
    SHORT_MESSAGE_TYPE_0,
    REPLACE_MESSAGE_TYPE_1,
    REPLACE_MESSAGE_TYPE_2,
    REPLACE_MESSAGE_TYPE_3,
    REPLACE_MESSAGE_TYPE_4,
    REPLACE_MESSAGE_TYPE_5,
    REPLACE_MESSAGE_TYPE_6,
    REPLACE_MESSAGE_TYPE_7,
    RETURN_CALL,
    ME_DATA_DOWNLOAD,
    ME_DEPERSONALIZE,
    SIM_DATA_DOWNLOAD
}

MessageClass

enum MessageClass { CLASS_0_FLASH, CLASS_1_ME, CLASS_2_SIM, CLASS_3_TE }

ContactGroup

interface ContactGroup {
    Integer getId()
    String getName()
    Integer getSize()
}

Contact

interface Contact {
    String getForenames()
    String getSurname()
    String getMsisdn()
    Integer[] getGroupIds()
    Integer getTimestamp()
}

ContactUpdateFlag

enum {
    DONT_NORMALIZE_MSISDNS,
    DONT_REPLACE_EXISTING
}

UserProfile

interface UserProfile {
    Integer getUserId()
    String getUsername()
    Decimal getCredits()
    String getHonorific()
    String getForenames()
    String getSurname()
    String getEmail()
    String getMsisdn()
    String getCountry()
    String getCompany()
    String getTimezone()
    Integer getDailyQuota()
    Integer getDailyQuotaUsed()
    String getJsonApiToken()
    String getMessageRelayUrl()
    String getStatusReportRelayUrl()
    Integer getLowCreditNotificationLevel()
    String getDefaultOriginAddress()
    String[] getOriginAddresses()
    UserProfileFlag[] getFlags()
}

UserProfileFlag

enum UserProfileFlag {
    HAS_FULL_ORIGIN_ADDRESS_CONTROL,
    CAN_TRANSFER_CREDITS,
    LOW_CREDIT_NOTIFY_EMAIL,
    LOW_CREDIT_NOTIFY_SMS
}

MessageSubmissionOptions

interface MessageSubmission {
    String uniqueId
    MessageSubmissionScheduling scheduling
    MessageSubmissionFlag[] flags
}

MessageSubmissionScheduling

interface MessageSubmissionScheduling {
    Iso8601 date
    String description
}

MessageSubmission

interface MessageSubmission {
    String sender
    String[] recipients
    Integer[] groups,
    String body,
    MessageBodyEncoding encoding,
    Ternary repliable,
    Integer routingProfile,
    Integer maxConcatParts,
    String clientId,
    Integer protocolId,
    MessageClass messageClass,
    StatusReportingMode statusReporting,
    MessageSubmissionTemplateDefaults templateDefaults
}

Address Types

In the JSON API you can specify origin and destination addresses as either a simple string, or an object like { address:<string>, type:<string> }. While the simple string method is sufficient, there are some benefits to supporting the complete version. One such benefit is making it simple to allow a user to only specify either an origin address or a repliable message.

The allow use of these address types, simply change the sender and recipients properties of the MessageSubmission to the suitable address type, and remove the repliable property. Also suitable methods should be added to the MessageSubmissionBuilder.

OriginAddress

interface OriginAddress {
    constant OriginAddress REPLIABLE = OriginAddress(address = "*", type = OriginAddressType.INTERNATIONAL)
    OriginAddressType type
    String address
}

OriginAddressType

enum OriginAddressType { INTERNATIONAL, NATIONAL, SHORTCODE, ALPHANUMERIC, UNSPECIFIED }

DestinationAddress

interface DestinationAddress {
    DestinationAddressType type
    String address
}

DestinationAddressType

enum DestinationAddressType { INTERNATIONAL, UNSPECIFIED }

MessageSubmissionFlag

enum MessageSubmissionFlag {
    ALLOW_DUPLICATE_RECIPIENTS,
    ALLOW_SCHEDULING_IN_PAST,
    ALLOW_INVALID_MSISDNS,
    REPLACE_INVALID_CHARACTERS,
    AUTO_CONCAT,
    AUTO_UNICODE
}

MessageSubmissionTemplateDefaults

interface MessageSubmissionTemplateDefaults {
    String h,
    String fn,
    String sn
}

MessageSubmissionResult

interface MessageSubmissionResult {
    MessageSubmissionStatus getStatus(),
    Integer getSubmissionId(),
    String getMessage()
}

MessageSubmissionStatus

enum MessageSubmissionStatus { IN_PROGRESS, SCHEDULED }

Message

interface Message {
    Integer getId()
    String getFrom()
    String getTo()
    Date getCreatedDate()
    String getBody()
    MessageBodyEncoding getEncoding()
    String getReferringClientId()
}

MessageState

interface MessageState {
    String getStatus(),
    Boolean isFinal(),
    String getMsisdn(),
    String getClientId()
}

BulkSMSClient

interface BulkSMSClient {
    setDefaultSubmissionFlags(MessageSubmissionFlag[] flags),
    MessageSubmissionResult sendMessage(String msisdn, String body),
    MessageSubmissionResult sendMessage(MessageSubmission submission),
    MessageSubmissionResult sendMessage(MessageSubmission[] submissions),
    MessageSubmissionResult sendMessage(MessageSubmission submission, MessageSubmissionOptions options),
    MessageSubmissionResult sendMessage(MessageSubmission[] submissions, MessageSubmissionOptions options),
    
    Decimal estimateMessage(String msisdn, String body),
    Decimal estimateMessage(MessageSubmission submission),
    Decimal estimateMessage(MessageSubmission[] submissions),
    Decimal estimateMessage(MessageSubmission submission, MessageSubmissionOptions options),
    Decimal estimateMessage(MessageSubmission[] submissions, MessageSubmissionOptions options),
    
    MessageState[] getMessageState(Integer submissionId, String msisdn = null, String clientId = null),
    
    Message[] getInbox(Integer lastRetrievedId = 0)
    
    ContactGroup[] getContactGroups(),
    Contact[] updateContacts(Contact[] contacts, ContactUpdateFlag[] flags),
    Contact[] getContacts(String filter = null, Integer offset = 0, Integer limit = 0),
    UserProfile getUserProfile()
}

MessageSubmissionBuilder

interface MessageSubmissionBuilder {
    MessageSubmissionBuilder from(String from),
    MessageSubmissionBuilder to(String to),
    MessageSubmissionBuilder toGroup(Integer groupId),
    MessageSubmissionBuilder body(String body),
    MessageSubmissionBuilder body(String body, MessageBodyEncoding encoding),
    MessageSubmissionBuilder routingProfile(Integer routingProfile),
    MessageSubmissionBuilder maxConcatParts(Integer maxConcatParts),
    MessageSubmissionBuilder clientId(String clientId),
    MessageSubmissionBuilder protocolId(ProtocolId protocolId),
    MessageSubmissionBuilder protocolId(Integer protocolId),
    MessageSubmissionBuilder messageClass(MessageClass messageClass),
    MessageSubmissionBuilder statusReportingMode(StatusReportingMode statusReportingMode),
    MessageSubmission build()
}