├── .gitignore
├── Procfile
├── database
└── couchdb
│ ├── _users
│ ├── _security.json
│ └── index.js
│ ├── enviar
│ ├── _security.json
│ └── index.js
│ └── _config.json.tmp
├── client
├── images
│ └── logo.png
├── models
│ ├── ui.js
│ ├── app.js
│ ├── user.js
│ └── convos.js
├── views
│ ├── require-auth.js
│ ├── layout.js
│ ├── login.js
│ ├── reset-password-init.js
│ ├── reset-password-confirm.js
│ ├── left-panel.js
│ ├── chat.js
│ └── account.js
├── components
│ ├── compose.js
│ ├── logo-area.js
│ ├── messages.js
│ └── conversation-list.js
├── util.js
└── index.js
├── .env.sample
├── test
├── helpers
│ ├── postmark.js
│ └── twilio.js
├── fixtures
│ ├── twilio
│ │ ├── inbound.txt
│ │ ├── inbound.json
│ │ ├── outbound.json
│ │ └── messages.json
│ ├── users
│ │ └── generator.js
│ └── formatted
│ │ └── messages.json
├── client
│ └── conversations.js
└── server
│ └── reset-password.js
├── server
├── receive-inbound.js
├── format-data.js
├── util.js
├── follow-outbound.js
├── fetch-messages.js
├── index.js
└── reset-password.js
├── app.json
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node server/index.js --harmony --use_strict
2 |
--------------------------------------------------------------------------------
/database/couchdb/_users/_security.json:
--------------------------------------------------------------------------------
1 | {
2 | "couchdb_auth_only": true
3 | }
4 |
--------------------------------------------------------------------------------
/client/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timwis/enviar/HEAD/client/images/logo.png
--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------
1 | COUCHDB_HOST=
2 | COUCHDB_USER=
3 | COUCHDB_PASS=
4 | TWILIO_ACCOUNT_SID=
5 | TWILIO_AUTH_TOKEN=
6 | TWILIO_PHONE=
7 | POSTMARK_SERVER_TOKEN=
8 | BASE_URL=
9 | FROM_EMAIL=
10 |
--------------------------------------------------------------------------------
/database/couchdb/enviar/_security.json:
--------------------------------------------------------------------------------
1 | {
2 | "admins": {
3 | "names": [],
4 | "roles": []
5 | },
6 | "members": {
7 | "names": [],
8 | "roles": [
9 | "agent"
10 | ]
11 | },
12 | "couchdb_auth_only": true
13 | }
14 |
--------------------------------------------------------------------------------
/test/helpers/postmark.js:
--------------------------------------------------------------------------------
1 | module.exports = { Client }
2 |
3 | function Client (token) {
4 | this.token = token
5 | }
6 |
7 | Client.prototype.sendEmail = (config, cb) => {
8 | console.log('Simulating email', config)
9 | cb(null, {})
10 | }
11 |
--------------------------------------------------------------------------------
/client/models/ui.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | namespace: 'ui',
3 | state: {},
4 | reducers: {
5 | set: (data, state) => {
6 | return data
7 | },
8 | reset: (property, state) => {
9 | return {[property]: null}
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/client/views/require-auth.js:
--------------------------------------------------------------------------------
1 | const { hasAgentAccess } = require('../util')
2 |
3 | module.exports = function RequireAuth (CurrentView) {
4 | return (state, prev, send) => {
5 | if (hasAgentAccess(state.user)) {
6 | return CurrentView(state, prev, send)
7 | } else {
8 | send('redirect', '/login')
9 | return document.createElement('div')
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/fixtures/twilio/inbound.txt:
--------------------------------------------------------------------------------
1 | ToCountry=US&ToState=PA&SmsMessageSid=SM5a5105b8c940a1kla4ebc7c8cf25fab5&NumMedia=0&ToCity=PHILADELPHIA&FromZip=19107&SmsSid=SM5a5105b8c940a1kla4ebc7c8cf25fab5&FromState=PA&SmsStatus=received&FromCity=PHILADELPHIA&Body=test2&FromCountry=US&To=%2B15851427131&ToZip=19107&NumSegments=1&MessageSid=SM5a5105b8c940a1kla4ebc7c8cf25fab5&AccountSid=ACf3072d52657d8f496d1e8c1c30e74b39&From=%2B13088632703&ApiVersion=2010-04-01
--------------------------------------------------------------------------------
/database/couchdb/enviar/index.js:
--------------------------------------------------------------------------------
1 | /* global emit */
2 | module.exports = {
3 | _id: '_design/messages',
4 | views: {
5 | byProviderId: {
6 | map: function (doc) {
7 | if (doc.providerId) {
8 | emit(doc.providerId, { _rev: doc._rev, status: doc.status })
9 | }
10 | }
11 | }
12 | },
13 | filters: {
14 | pendingOutbound: function (doc, req) {
15 | return !doc.providerId && doc.direction === 'outbound'
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/database/couchdb/_config.json.tmp:
--------------------------------------------------------------------------------
1 | {
2 | "couch_httpd_auth": {
3 | "timeout": 7200
4 | },
5 | "httpd": {
6 | "enable_cors": true
7 | },
8 | "cors": {
9 | "origins": "*",
10 | "credentials": true,
11 | "methods": [
12 | "GET",
13 | "PUT",
14 | "POST",
15 | "HEAD",
16 | "DELETE"
17 | ],
18 | "headers": [
19 | "accept",
20 | "authorization",
21 | "content-type",
22 | "origin",
23 | "referer",
24 | "x-csrf-token"
25 | ]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/server/receive-inbound.js:
--------------------------------------------------------------------------------
1 | const formatData = require('./format-data')
2 | const respond = require('./util').respond
3 |
4 | module.exports = receiveInbound
5 |
6 | // Handle inbound messages from twilio webhooks
7 | function receiveInbound (db) {
8 | return function receiveInboundHandler (req, res) {
9 | const formattedMessage = formatData.fromTwilioWebhook(req.body)
10 |
11 | db.insert(formattedMessage, (err, body) => {
12 | if (err) respond(res, 500, 'Error inserting inbound message into db')
13 |
14 | console.log('inbound', formattedMessage)
15 | res.end()
16 | })
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/database/couchdb/_users/index.js:
--------------------------------------------------------------------------------
1 | /* global emit */
2 | module.exports = {
3 | _id: '_design/users',
4 | validate_doc_update: function (newDoc, oldDoc, userCtx, secObj) {
5 | if (newDoc._deleted !== true && (newDoc.name.indexOf('@') === -1 || newDoc.name.indexOf('.') === -1)) {
6 | throw({ forbidden: 'Username must be an email' }) // eslint-disable-line
7 | }
8 | },
9 | views: {
10 | byResetToken: {
11 | map: function (doc) {
12 | if (doc.metadata.resetToken) {
13 | emit(doc.metadata.resetToken.token, doc.metadata.resetToken.created)
14 | }
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/fixtures/twilio/inbound.json:
--------------------------------------------------------------------------------
1 | {
2 | "ToCountry": "US",
3 | "ToState": "PA",
4 | "SmsMessageSid": "SM5a5105b8c940a1kla4ebc7c8cf25fab5",
5 | "NumMedia": "0",
6 | "ToCity": "PHILADELPHIA",
7 | "FromZip": "19107",
8 | "SmsSid": "SM5a5105b8c940a1kla4ebc7c8cf25fab5",
9 | "FromState": "PA",
10 | "SmsStatus": "received",
11 | "FromCity": "PHILADELPHIA",
12 | "Body": "incoming!",
13 | "FromCountry": "US",
14 | "To": "+15851427131",
15 | "ToZip": "19107",
16 | "NumSegments": "1",
17 | "MessageSid": "SM5a5105b8c940a1kla4ebc7c8cf25fab5",
18 | "AccountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
19 | "From": "+13088632703",
20 | "ApiVersion": "2010-04-01"
21 | }
--------------------------------------------------------------------------------
/test/fixtures/users/generator.js:
--------------------------------------------------------------------------------
1 | exports.matchWithEmail = function (email) {
2 | return {
3 | rows: [
4 | {
5 | doc: {
6 | metadata: {
7 | email: 'foo@bar.com'
8 | }
9 | }
10 | }
11 | ]
12 | }
13 | }
14 |
15 | exports.matchWithToken = function (email, token, minutesAgo) {
16 | const created = Date.now() - minutesAgo * 60e3
17 | return {
18 | rows: [
19 | {
20 | value: created,
21 | doc: {
22 | metadata: {
23 | email,
24 | resetToken: {
25 | created,
26 | token
27 | }
28 | }
29 | }
30 | }
31 | ]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/client/components/compose.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const css = require('sheetify')
3 |
4 | const prefix = css`
5 | :host {
6 | height: 75%;
7 | margin: 10px;
8 | }
9 | #body {
10 | width: 100%;
11 | line-height: 2;
12 | }
13 | `
14 |
15 | module.exports = (cb) => {
16 | return html`
17 |
`
20 |
21 | function onSubmit (e) {
22 | const body = e.target.querySelector('#body')
23 | if (body.value && cb) {
24 | const formData = { body: body.value }
25 | cb(formData)
26 | body.value = ''
27 | }
28 | e.preventDefault()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test/helpers/twilio.js:
--------------------------------------------------------------------------------
1 | const messages = require('../fixtures/twilio/messages.json')
2 | const outbound = require('../fixtures/twilio/outbound.json')
3 |
4 | module.exports = {
5 | messages: {
6 | get: (params, cb) => {
7 | setTimeout(() => cb(null, messages), 500)
8 | },
9 | post: (data, cb) => {
10 | const response = Object.assign({}, outbound)
11 | response.body = data.body
12 | response.to = data.To
13 | response.dateCreated = (new Date()).toISOString()
14 | response.sid = 'STUB' + guid()
15 | setTimeout(() => cb(null, response), 500)
16 | }
17 | }
18 | }
19 |
20 | function guid () {
21 | function s4 () {
22 | return Math.floor((1 + Math.random()) * 0x10000)
23 | .toString(16)
24 | .substring(1)
25 | }
26 | return s4() + s4() + s4() + s4() + s4() + s4() + s4() + s4()
27 | }
28 |
--------------------------------------------------------------------------------
/server/format-data.js:
--------------------------------------------------------------------------------
1 | const shortid = require('shortid')
2 |
3 | exports.fromTwilioRest = function (msg) {
4 | const timestamp = Date.parse(msg.dateCreated)
5 | return {
6 | _id: `msg-${timestamp}-${shortid.generate()}`,
7 | providerId: msg.sid,
8 | date: msg.dateCreated,
9 | from: msg.from,
10 | to: msg.to,
11 | body: msg.body,
12 | status: msg.status,
13 | direction: msg.direction === 'inbound' ? 'inbound' : 'outbound'
14 | }
15 | }
16 |
17 | exports.fromTwilioWebhook = function (msg) {
18 | const date = new Date()
19 | return {
20 | _id: `msg-${date.getTime()}-${shortid.generate()}`,
21 | providerId: msg.SmsSid,
22 | date: date.toISOString(),
23 | from: msg.From,
24 | to: msg.To,
25 | body: msg.Body,
26 | status: msg.SmsStatus,
27 | direction: 'inbound'
28 | }
29 | }
30 |
31 | exports.toTwilioRest = function (msg) {
32 | return {
33 | To: msg.to,
34 | body: msg.body
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/client/views/layout.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const css = require('sheetify')
3 |
4 | const LeftPanel = require('./left-panel')
5 | const { hasAgentAccess } = require('../util')
6 |
7 | const prefix = css`
8 | :host {
9 | height: 100%;
10 | display: flex;
11 | }
12 | .left {
13 | width: 200px;
14 | overflow-y: auto;
15 | background-color: #2F4550;
16 | }
17 | .right {
18 | height: 100%;
19 | flex: 1 auto;
20 | display: flex;
21 | flex-direction: column;
22 | }
23 | `
24 |
25 | module.exports = (CurrentView) => (state, prev, send) => {
26 | const leftPanel = LeftPanel(state, prev, send)
27 | leftPanel.classList.add('left')
28 |
29 | const currentView = CurrentView ? CurrentView(state, prev, send) : html``
30 | currentView.classList.add('right')
31 |
32 | return html`
33 | hasAgentAccess(state.user) && send('convos:fetch')} class=${prefix}>
34 | ${leftPanel}
35 | ${currentView}
36 |
`
37 | }
38 |
--------------------------------------------------------------------------------
/server/util.js:
--------------------------------------------------------------------------------
1 | const url = require('url')
2 |
3 | exports.respond = function respond (res, statusCode, message) {
4 | if (statusCode) {
5 | res.statusCode = statusCode
6 | }
7 | if (message) {
8 | res.setHeader('Content-Type', 'application/json')
9 | return res.end(JSON.stringify({ message }))
10 | }
11 | return res.end()
12 | }
13 |
14 | exports.parseBody = function parseBody (parser, handler) {
15 | return function (req, res) {
16 | parser(req, (err, body) => {
17 | if (err) return exports.respond(res, 400, 'Error parsing request body')
18 | req.body = body
19 | handler(req, res)
20 | })
21 | }
22 | }
23 |
24 | exports.addAuthToUrl = function addAuthToUrl (plainUrl, user, pass) {
25 | const urlObj = url.parse(plainUrl)
26 | urlObj.auth = user + ':' + pass
27 | return url.format(urlObj)
28 | }
29 |
30 | // bankai returns http route handlers that return a stream
31 | exports.pipeToResponse = function pipeToResponse (handler) {
32 | return (req, res) => handler(req, res).pipe(res)
33 | }
34 |
--------------------------------------------------------------------------------
/client/views/login.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const getFormData = require('get-form-data')
3 | const css = require('sheetify')
4 |
5 | const prefix = css`
6 | :host {
7 | height: 100%;
8 | background-color: #F4F4F9;
9 | padding: 0 15px;
10 | }
11 | `
12 |
13 | module.exports = (state, prev, send) => {
14 | return html`
15 | `
32 |
33 | function onSubmit (e) {
34 | const formData = getFormData(e.target)
35 | send('user:login', formData)
36 | e.preventDefault()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "enviar",
3 | "description": "Chat interface for SMS / text messages",
4 | "repository": "https://github.com/timwis/enviar",
5 | "logo": "https://i.imgur.com/w6dveCM.png",
6 | "env": {
7 | "COUCHDB_HOST": {
8 | "description": "Hostname of CouchDB instance"
9 | },
10 | "COUCHDB_USER": {
11 | "description": "CouchDB username"
12 | },
13 | "COUCHDB_PASS": {
14 | "description": "CouchDB password"
15 | },
16 | "TWILIO_ACCOUNT_SID": {
17 | "description": "Twilio Account SID"
18 | },
19 | "TWILIO_AUTH_TOKEN": {
20 | "description": "Twilio Auth Token"
21 | },
22 | "TWILIO_PHONE": {
23 | "description": "Twilio Phone Number, with leading +"
24 | },
25 | "POSTMARK_SERVER_TOKEN": {
26 | "description": "Server token from postmark.com for sending emails"
27 | },
28 | "FROM_EMAIL": {
29 | "description": "Email address to send password reset emails from"
30 | },
31 | "BASE_URL": {
32 | "description": "Website URL, ie. http://mysite.com"
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/server/follow-outbound.js:
--------------------------------------------------------------------------------
1 | const formatData = require('./format-data')
2 |
3 | module.exports = followOutbound
4 |
5 | // Subscribe to pending outbound messages and send them to twilio
6 | function followOutbound (db, twilio, phone) {
7 | const feed = db.follow({
8 | filter: 'messages/pendingOutbound',
9 | include_docs: true
10 | })
11 |
12 | feed.on('change', (change) => {
13 | const payload = formatData.toTwilioRest(change.doc)
14 | payload.From = phone
15 |
16 | // Dispatch to twilio
17 | twilio.messages.post(payload, (err, response) => {
18 | if (err) return console.error('Error sending message to provider')
19 |
20 | const formattedResponse = formatData.fromTwilioRest(response)
21 |
22 | // Update existing doc instead of inserting a new one
23 | formattedResponse._id = change.id
24 | formattedResponse._rev = change.doc._rev
25 |
26 | db.insert(formattedResponse, (err, body) => {
27 | if (err) return console.error('Error updating message in db', err)
28 | console.log('outbound', formattedResponse)
29 | })
30 | })
31 | })
32 | feed.follow()
33 | return feed
34 | }
35 |
--------------------------------------------------------------------------------
/client/components/logo-area.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const css = require('sheetify')
3 |
4 | const prefix = css`
5 | .pure-menu-selected a,
6 | .pure-menu-link:hover,
7 | .pure-menu-link:focus {
8 | background-color: #F4F4F9;
9 | color: #000;
10 | }
11 | .pure-menu-link,
12 | .pure-menu-heading {
13 | color: #B8DBD9;
14 | }
15 | .pure-menu-heading {
16 | font-size: 150%;
17 | padding: 10px 15px 0 15px;
18 | text-transform: inherit;
19 | }
20 | .pure-menu-item a {
21 | text-overflow: ellipsis;
22 | white-space: nowrap;
23 | overflow: hidden;
24 | }
25 | `
26 |
27 | module.exports = (title, user = {}) => {
28 | const loginLogoutLink = user.name
29 | ? html``
30 | : html``
31 |
32 | return html`
33 | `
41 | }
42 |
--------------------------------------------------------------------------------
/client/views/reset-password-init.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const css = require('sheetify')
3 |
4 | const prefix = css`
5 | :host {
6 | height: 100%;
7 | background-color: #F4F4F9;
8 | padding: 0 15px;
9 | }
10 | `
11 |
12 | module.exports = (state, prev, send) => {
13 | return html`
14 |
15 | Reset password
16 |
28 | `
29 |
30 | function onSubmit (e) {
31 | const input = e.target.querySelector('[name=email]')
32 | const email = input.value
33 | send('user:resetPasswordInit', { email })
34 | input.value = ''
35 | e.preventDefault()
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/client/components/messages.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const timeago = require('timeago.js')()
3 | const css = require('sheetify')
4 |
5 | const prefix = css`
6 | ul {
7 | list-style: none;
8 | padding: 10px;
9 | }
10 |
11 | li {
12 | display: flex;
13 | margin: 10px 0;
14 | }
15 |
16 | li.outbound {
17 | justify-content: flex-end;
18 | align-items: flex-end;
19 | }
20 |
21 | div.content {
22 | padding: 10px;
23 | border-radius: 2px;
24 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
25 | }
26 |
27 | li.outbound div.content {
28 | background-color: #7B9AAD;
29 | }
30 |
31 | li.inbound div.content {
32 | background-color: #B8DBD9;
33 | }
34 |
35 | abbr {
36 | color: #616161;
37 | }
38 | `
39 |
40 | module.exports = (phone, messages, onSendMsg) => {
41 | return html`
42 |
43 |
44 | ${messages.map((msg) => html`
45 | -
46 |
47 |
${msg.body}
48 | ${msg.date ? html`
${timeago.format(msg.date)}` : ''}
49 |
50 | `)}
51 |
52 | `
53 | }
54 |
--------------------------------------------------------------------------------
/test/fixtures/twilio/outbound.json:
--------------------------------------------------------------------------------
1 | {
2 | "sid": "SM71cadf6d66bb4fc68833c641d7f7fda7",
3 | "date_created": "Mon, 01 Aug 2016 10:02:53 +0000",
4 | "date_updated": "Mon, 01 Aug 2016 10:02:53 +0000",
5 | "date_sent": null,
6 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
7 | "to": "+13088632703",
8 | "from": "+15851427131",
9 | "messaging_service_sid": null,
10 | "body": "Outbound fixture",
11 | "status": "queued",
12 | "num_segments": "1",
13 | "num_media": "0",
14 | "direction": "outbound-api",
15 | "api_version": "2010-04-01",
16 | "price": null,
17 | "price_unit": "USD",
18 | "error_code": null,
19 | "error_message": null,
20 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM71cadf6d66bb4fc01139c641d7f7fda7.json",
21 | "subresource_uris": {
22 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM71cadf6d66bb4fc01139c641d7f7fda7/Media.json"
23 | },
24 | "dateCreated": "2016-08-01T10:02:53.000Z",
25 | "dateUpdated": "2016-08-01T10:02:53.000Z",
26 | "dateSent": null,
27 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
28 | "messagingServiceSid": null,
29 | "numSegments": "1",
30 | "numMedia": "0",
31 | "apiVersion": "2010-04-01",
32 | "priceUnit": "USD",
33 | "errorCode": null,
34 | "errorMessage": null,
35 | "subresourceUris": {
36 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM71cadf6d66bb4fc01139c641d7f7fda7/Media.json"
37 | }
38 | }
--------------------------------------------------------------------------------
/client/views/reset-password-confirm.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const css = require('sheetify')
3 | const getFormData = require('get-form-data')
4 |
5 | const prefix = css`
6 | :host {
7 | height: 100%;
8 | background-color: #F4F4F9;
9 | padding: 0 15px;
10 | }
11 | `
12 |
13 | module.exports = (state, prev, send) => {
14 | return html`
15 |
16 | Reset password
17 |
34 | `
35 |
36 | function onSubmit (e) {
37 | const formData = getFormData(e.target)
38 | if (formData.password === formData.confirm) {
39 | formData.token = state.params.token
40 | send('user:resetPasswordConfirm', formData)
41 |
42 | // reset inputs
43 | Array.from(document.querySelectorAll('input')).forEach((el) => el.value = '')
44 | }
45 | e.preventDefault()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/client/models/app.js:
--------------------------------------------------------------------------------
1 | const series = require('run-series')
2 | const Push = require('push.js')
3 |
4 | const VALID_ROLES = ['agent', '_admin']
5 |
6 | module.exports = (db) => ({
7 | state: {},
8 | reducers: {},
9 | effects: {
10 | initialize: (data, state, send, done) => {
11 | db.getSession((err, body) => {
12 | if (err) {
13 | // Error with request
14 | return done(new Error('Error getting current session'))
15 | } else if (!hasAgentAccess(body.userCtx)) {
16 | // Not logged in
17 | send('redirect', '/login', done)
18 | } else {
19 | // Logged in
20 | series([
21 | (cb) => send('user:set', body.userCtx, cb),
22 | (cb) => send('convos:fetch', cb)
23 | ], done)
24 | }
25 | })
26 | },
27 | redirect: (path, state, send, done) => {
28 | window.history.pushState({}, null, path)
29 | send('location:setLocation', { location: path }, done)
30 | },
31 | pushNotification: (data, state, send, done) => {
32 | const path = '/' + data.from
33 | Push.create(data.from, {
34 | body: data.body,
35 | icon: 'https://i.imgur.com/w6dveCM.png',
36 | onClick: function () {
37 | window.focus()
38 | send('redirect', path, done)
39 | this.close()
40 | }
41 | })
42 | done()
43 | }
44 | }
45 | })
46 |
47 | function hasAgentAccess (userCtx) {
48 | return userCtx.roles.some((role) => VALID_ROLES.indexOf(role) !== -1)
49 | }
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "enviar",
3 | "version": "1.1.0",
4 | "description": "Chat interface for SMS / text messages",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server/index.js",
8 | "test": "ava --verbose",
9 | "bootstrap": "node database/bootstrap.js",
10 | "cors": "add-cors-to-couchdb",
11 | "user": "node database/user.js"
12 | },
13 | "author": "timwis ",
14 | "license": "MIT",
15 | "repository": "timwis/enviar",
16 | "keywords": [
17 | "sms",
18 | "text",
19 | "inbox",
20 | "chat"
21 | ],
22 | "dependencies": {
23 | "add-cors-to-couchdb": "0.0.6",
24 | "bankai": "^3.3.0",
25 | "body": "^5.1.0",
26 | "browserify": "^13.1.0",
27 | "choo": "^3.2.0",
28 | "choo-log": "^1.4.0",
29 | "common-tags": "^1.3.1",
30 | "couchdb-bootstrap": "^1.14.0",
31 | "dotenv": "^2.0.0",
32 | "envify": "^3.4.1",
33 | "font-awesome": "^4.6.3",
34 | "get-form-data": "^1.2.5",
35 | "insert-css": "^0.2.0",
36 | "lodash": "^4.14.1",
37 | "meow": "^3.7.0",
38 | "nano": "^6.2.0",
39 | "postmark": "^1.2.1",
40 | "pouchdb": "^5.4.5",
41 | "pouchdb-authentication": "^0.5.4",
42 | "purecss": "^0.6.0",
43 | "push.js": "0.0.11",
44 | "run-parallel": "^1.1.6",
45 | "run-series": "^1.1.4",
46 | "server-router": "^2.1.0",
47 | "sheetify": "^5.0.5",
48 | "shortid": "^2.2.6",
49 | "timeago.js": "^1.0.5",
50 | "twilio": "^2.9.1",
51 | "uuid": "^3.0.0",
52 | "xtend": "^4.0.1"
53 | },
54 | "devDependencies": {
55 | "ava": "^0.16.0",
56 | "jsdom": "^9.4.1",
57 | "jsdom-global": "^2.0.0",
58 | "testdouble": "^1.6.1"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/client/views/left-panel.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const values = require('lodash/values')
3 | const groupBy = require('lodash/groupBy')
4 |
5 | const ConversationList = require('../components/conversation-list')
6 | const LogoArea = require('../components/logo-area')
7 | const { validatePhone } = require('../util')
8 |
9 | const title = process.env.APP_TITLE || require('../../package.json').name
10 |
11 | module.exports = (state, prev, send) => {
12 | const isAdding = state.convos.isAdding
13 | const activePhone = state.params && state.params.phone
14 | const conversations = groupByConversation(state.convos.messages)
15 | const phones = calculateUnreadCounts(conversations, state.convos.lastRead)
16 |
17 | return html`
18 |
19 | ${LogoArea(title, state.user)}
20 | ${ConversationList({ phones, activePhone, isAdding, onClickAdd, onSubmitAdd })}
21 |
`
22 |
23 | function groupByConversation (messages) {
24 | return groupBy(
25 | values(messages),
26 | (msg) => msg.direction === 'inbound' ? msg.from : msg.to
27 | )
28 | }
29 |
30 | // Calculate number of unread messages for each conversation
31 | // returns: [ { label: '+12151231234', unread: 2 } ]
32 | function calculateUnreadCounts (conversations, lastRead) {
33 | const phones = []
34 | for (let phone in conversations) {
35 | const unread = lastRead[phone]
36 | ? conversations[phone].filter((msg) => msg.date > lastRead[phone]).length
37 | : conversations[phone].length
38 | phones.push({ label: phone, unread })
39 | }
40 | return phones
41 | }
42 |
43 | function onClickAdd () {
44 | send('convos:setAdding', true)
45 | }
46 |
47 | function onSubmitAdd (phone) {
48 | const validatedPhone = validatePhone(phone)
49 | if (validatedPhone) {
50 | send('convos:addConversation', validatedPhone)
51 | return true
52 | } else {
53 | console.log('Invalid phone number')
54 | return false
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/client/util.js:
--------------------------------------------------------------------------------
1 | const VALID_ROLES = ['agent', '_admin']
2 |
3 | exports.hasAgentAccess = function hasAgentAccess (userCtx) {
4 | return userCtx.roles.some((role) => VALID_ROLES.indexOf(role) !== -1)
5 | }
6 |
7 | exports.hasAdminAccess = function hasAdminAccess (userCtx) {
8 | return userCtx.roles.indexOf('_admin') !== -1
9 | }
10 |
11 | exports.formatPhone = function formatPhone (phone) {
12 | return `(${phone.substring(2, 5)}) ${phone.substring(5, 8)}-${phone.substring(8)}`
13 | }
14 |
15 | exports.validatePhone = function validatePhone (input) {
16 | const numbers = input.replace(/\D/g, '')
17 | if (numbers.length === 10) {
18 | return '+1' + numbers
19 | } else if (numbers.length === 11 && numbers[0] === 1) {
20 | return '+' + numbers
21 | } else {
22 | return false
23 | }
24 | }
25 |
26 | exports.localStorageWrapper = function localStorageWrapper (key, value, cb) {
27 | // if (!cb && typeof value === 'function') cb = value
28 | // const stringValue = typeof value === 'string' ? value : JSON.stringify(value)
29 | if (!cb && typeof value === 'function') {
30 | // Get
31 | cb = value
32 | try {
33 | const stringResult = window.localStorage.getItem(key)
34 | if (stringResult) {
35 | try {
36 | const parsedResult = JSON.parse(stringResult)
37 | return cb(null, parsedResult)
38 | } catch (e) {
39 | return cb(null, stringResult)
40 | }
41 | }
42 | return cb()
43 | } catch (e) {
44 | return cb(new Error('Error retrieving item from localStorage'))
45 | }
46 | } else {
47 | // Set
48 | let stringValue
49 | if (typeof value === 'string') {
50 | stringValue = value
51 | } else {
52 | try {
53 | stringValue = JSON.stringify(value)
54 | } catch (e) {
55 | return cb(new Error('Error converting value to string'))
56 | }
57 | }
58 | try {
59 | window.localStorage.setItem(key, stringValue)
60 | return cb()
61 | } catch (e) {
62 | return cb(new Error('Error saving value to localStorage'))
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | const choo = require('choo')
2 | const css = require('sheetify')
3 | const PouchDB = require('pouchdb')
4 | PouchDB.plugin(require('pouchdb-authentication'))
5 |
6 | const Layout = require('./views/layout')
7 | const RequireAuth = require('./views/require-auth')
8 | const Login = require('./views/login')
9 | const Chat = require('./views/chat')
10 | const Account = require('./views/account')
11 | const ResetPasswordInit = require('./views/reset-password-init')
12 | const ResetPasswordConfirm = require('./views/reset-password-confirm')
13 |
14 | css('purecss/build/base')
15 | css('purecss/build/forms')
16 | css('purecss/build/buttons')
17 | require('insert-css')(`
18 | @import url(https://fonts.googleapis.com/css?family=Roboto);
19 | @import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css);
20 | html, body {
21 | height: 100%;
22 | margin: 0;
23 | padding: 0;
24 | }
25 | body {
26 | font-family: 'Roboto', sans-serif;
27 | overflow: hidden;
28 | }
29 | `)
30 |
31 | const app = choo()
32 |
33 | if (process.env.NODE_ENV === 'development') {
34 | const log = require('choo-log')
35 | app.use(log())
36 | }
37 |
38 | const dbURL = process.env.COUCHDB_HOST + '/enviar'
39 | const db = new PouchDB(dbURL, { skipSetup: true })
40 |
41 | // Check for user session and pass it to user model as initial state
42 | // Not very chooey but no other option panned out.
43 | db.getSession((err, body) => {
44 | const initialUserState = err ? {} : body.userCtx
45 | app.model(require('./models/user')(db, initialUserState))
46 | app.model(require('./models/app')(db))
47 | app.model(require('./models/convos')(db, initialUserState))
48 | app.model(require('./models/ui'))
49 |
50 | app.router((route) => [
51 | route('/', RequireAuth(Layout())),
52 | route('/:phone', RequireAuth(Layout(Chat))),
53 | route('/login', Layout(Login)),
54 | route('/account', RequireAuth(Layout(Account))),
55 | route('/reset-password', Layout(ResetPasswordInit), [
56 | route('/:token', Layout(ResetPasswordConfirm))
57 | ])
58 | ])
59 |
60 | const tree = app.start()
61 | document.body.appendChild(tree)
62 | })
63 |
--------------------------------------------------------------------------------
/client/views/chat.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const css = require('sheetify')
3 | const compose = require('lodash/fp/compose')
4 | const values = require('lodash/fp/values')
5 | const sortBy = require('lodash/fp/sortBy')
6 | const filter = require('lodash/fp/filter')
7 |
8 | const Messages = require('../components/messages')
9 | const Compose = require('../components/compose')
10 | const { formatPhone } = require('../util')
11 |
12 | const prefix = css`
13 | .messages {
14 | flex: 1 auto;
15 | display: flex;
16 | flex-direction: column-reverse;
17 | background-color: #F4F4F9;
18 | overflow-y: auto;
19 | }
20 | .header {
21 | font-size: 150%;
22 | padding: 5px;
23 | background-color: #F4F4F9;
24 | border-bottom: 1px black solid;
25 | }
26 | .compose {
27 | height: 75px;
28 | background-color: #F4F4F9;
29 | }
30 | `
31 |
32 | module.exports = (state, prev, send) => {
33 | const activePhone = state.params && state.params.phone
34 | const conversation = getConversation(state.convos.messages, activePhone)
35 | const messages = Messages(activePhone, conversation)
36 | setLastRead(activePhone, conversation, state.convos.lastRead)
37 |
38 | return html`
39 |
40 |
43 |
44 | ${messages}
45 |
46 |
47 | ${Compose(onCompose)}
48 |
49 |
`
50 |
51 | function getConversation (messages, phone) {
52 | return compose(
53 | values,
54 | filter((msg) => msg.from === phone || msg.to === phone),
55 | sortBy('date')
56 | )(messages)
57 | }
58 |
59 | function onCompose (data) {
60 | data.to = activePhone
61 | send('convos:sendOutbound', data)
62 | }
63 |
64 | // Check if active conversation's latest message is newer than
65 | // the last "read" message date. If so, mark it as the latest "read"
66 | // message date.
67 | function setLastRead (phone, messages, lastRead) {
68 | const lastMessage = messages.length && messages[messages.length - 1]
69 | if (lastMessage && (!lastRead[phone] || lastRead[phone] < lastMessage.date)) {
70 | send('convos:saveLastRead', { phone, date: lastMessage.date })
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/client/components/conversation-list.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const css = require('sheetify')
3 |
4 | const formatPhone = require('../util').formatPhone
5 |
6 | css('purecss/build/menus')
7 |
8 | const prefix = css`
9 | :host {
10 | position: relative;
11 | }
12 | .pure-menu-selected a,
13 | .pure-menu-link:hover,
14 | .pure-menu-link:focus {
15 | background-color: #F4F4F9;
16 | color: #000;
17 | }
18 | .pure-menu-link,
19 | .pure-menu-heading {
20 | color: #B8DBD9;
21 | }
22 | .align-right {
23 | position: absolute;
24 | right: 15px;
25 | top: 8px;
26 | }
27 | .new-conversation {
28 | color: #B8DBD9;
29 | }
30 | .unread-count {
31 | font-size: 80%;
32 | padding: 3px 9px;
33 | background-color: #f4f4f9;
34 | color: #000;
35 | border-radius: 10px;
36 | }
37 | `
38 |
39 | module.exports = ({ phones, activePhone, isAdding, onClickAdd, onSubmitAdd }) => {
40 | return html`
41 | `
49 |
50 | function listItem (phone) {
51 | const classes = ['pure-menu-item']
52 | if (activePhone && activePhone === phone.label) {
53 | classes.push('pure-menu-selected')
54 | }
55 |
56 | return html`
57 |
58 |
62 | `
63 | }
64 |
65 | function clickAdd (e) {
66 | if (onClickAdd) onClickAdd()
67 | e.preventDefault()
68 | }
69 |
70 | function addForm () {
71 | return html`
72 | `
75 | }
76 |
77 | function submitAdd (e) {
78 | const phone = e.target.querySelector('#phone')
79 | if (phone.value && onSubmitAdd) {
80 | if (onSubmitAdd(phone.value)) {
81 | // clear value if submit was valid/successful
82 | phone.value = ''
83 | }
84 | }
85 | e.preventDefault()
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/client/views/account.js:
--------------------------------------------------------------------------------
1 | const html = require('choo/html')
2 | const getFormData = require('get-form-data')
3 | const css = require('sheetify')
4 |
5 | const { hasAdminAccess } = require('../util')
6 |
7 | const prefix = css`
8 | :host {
9 | height: 100%;
10 | background-color: #F4F4F9;
11 | padding: 0 15px;
12 | }
13 | #logout {
14 | width: 200px;
15 | }
16 | #current-login {
17 | margin-top: 0;
18 | }
19 | `
20 |
21 | module.exports = (state, prev, send) => {
22 | return html`
23 |
24 | Logout
25 | Logged in as ${state.user.name}
26 |
30 |
31 | Change password
32 |
45 |
46 | ${hasAdminAccess(state.user)
47 | ? html`
48 |
49 |
Invite new user
50 |
62 |
`
63 | : ''}
64 |
65 | `
66 |
67 | function onChangePassword (e) {
68 | const formData = getFormData(e.target)
69 | if (formData.password === formData.confirm) {
70 | send('user:changePassword', formData)
71 | } else {
72 | console.error('Passwords do not match')
73 | }
74 | e.preventDefault()
75 | }
76 |
77 | function onInvite (e) {
78 | const input = e.target.querySelector('[name=email]')
79 | const email = input.value
80 | if (email) {
81 | send('user:invite', { email })
82 | input.value = ''
83 | }
84 | e.preventDefault()
85 | }
86 |
87 | function onClickLogout (e) {
88 | send('user:logout')
89 | e.preventDefault()
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/server/fetch-messages.js:
--------------------------------------------------------------------------------
1 | const keyBy = require('lodash/keyBy')
2 |
3 | const formatData = require('./format-data')
4 |
5 | module.exports = seedWithMessages
6 |
7 | // Fetch messages since last run (or a full page if empty db)
8 | function seedWithMessages (db, twilio) {
9 | db.list({
10 | descending: true,
11 | limit: 1,
12 | include_docs: true,
13 | endkey: 'msg-' // '_' comes before 'm' in ASCII sort
14 | }, (err, body) => {
15 | if (err) return console.error(err)
16 |
17 | const fetchOpts = {} // By default, fetch a page of most recent messages
18 | if (body.rows.length) {
19 | // If db already contains message records, fetch records since latest one
20 | console.log('most recent message', body.rows[0])
21 | const mostRecentDate = body.rows[0].doc.date
22 | fetchOpts['DateSent>'] = mostRecentDate
23 | }
24 | twilio.messages.get(fetchOpts, formatAndInsert)
25 | })
26 |
27 | function formatAndInsert (err, response) {
28 | if (err) return console.error('Error fetching messages from twilio')
29 |
30 | const fetchedMessages = response.messages.map(formatData.fromTwilioRest)
31 | const fetchedProviderIds = fetchedMessages.map((msg) => msg.providerId)
32 |
33 | // Check if messages are already stored in local db
34 | db.view('messages', 'byProviderId', { keys: fetchedProviderIds }, (err, body) => {
35 | if (err) return console.error('Error querying view', err)
36 |
37 | const updateBatch = detectUpdates(fetchedMessages, body.rows)
38 |
39 | if (updateBatch.length) {
40 | db.bulk({ docs: updateBatch }, (err, body) => {
41 | if (err) return console.error('Error inserting messages into database', err)
42 | console.log(body)
43 | })
44 | }
45 | })
46 | }
47 | }
48 |
49 | // For each message from the provider, check for a match in the local db.
50 | // If any changes, get its _id and _rev to update the record instead of
51 | // inserting a new one. But if no changes, just discard the record from
52 | // the update batch. If no match, just insert the record as-is.
53 | function detectUpdates (fetched, existing) {
54 | const existingByProviderId = keyBy(existing, 'key')
55 |
56 | return fetched.map((msg) => {
57 | const match = existingByProviderId[msg.providerId]
58 | if (match) {
59 | if (msg.status !== match.value.status) {
60 | // Changes detected (we only care about status at the moment)
61 | msg._id = match.id
62 | msg._rev = match.value._rev
63 | } else {
64 | // Since map doesn't let us remove the record, we set this for .filter()
65 | msg.noChanges = true
66 | }
67 | }
68 | return msg
69 | }).filter((msg) => !msg.noChanges)
70 | }
71 |
--------------------------------------------------------------------------------
/client/models/user.js:
--------------------------------------------------------------------------------
1 | const series = require('run-series')
2 | const http = require('choo/http')
3 | const uuid = require('uuid')
4 |
5 | module.exports = (db, initialState) => ({
6 | namespace: 'user',
7 | state: initialState || {
8 | name: '',
9 | roles: []
10 | },
11 | reducers: {
12 | set: (userCtx, state) => {
13 | return userCtx
14 | },
15 | reset: (data, state) => {
16 | return module.exports().state
17 | }
18 | },
19 | effects: {
20 | login: (data, state, send, done) => {
21 | const { email, password } = data
22 | db.login(email, password, (err, body) => {
23 | if (err) return done(new Error('Login error'))
24 | window.location.href = '/' // force refresh to call subscriptions again
25 | })
26 | },
27 | logout: (data, state, send, done) => {
28 | db.logout((err) => {
29 | if (err) return done(new Error('Error logging out'))
30 | series([
31 | (cb) => send('user:reset', cb),
32 | (cb) => send('convos:reset', cb),
33 | (cb) => send('redirect', '/login', cb)
34 | ], done)
35 | })
36 | },
37 | changePassword: (data, state, send, done) => {
38 | db.changePassword(state.name, data.password, (err, body) => {
39 | if (err) return done(new Error('Error changing password'))
40 | const loginData = { email: state.name, password: data.password }
41 | send('user:login', loginData, done)
42 | })
43 | },
44 | resetPasswordInit: (data, state, send, done) => {
45 | http.post({
46 | uri: '/api/reset-password-init',
47 | json: data
48 | }, (err, response, body) => {
49 | if (err || response.statusCode !== 200) return done(new Error('Error initializing password reset'))
50 | send('ui:set', { resetPasswordInitSubmitted: true }, done)
51 | })
52 | },
53 | resetPasswordConfirm: (data, state, send, done) => {
54 | http.post({
55 | uri: '/api/reset-password-confirm',
56 | json: data
57 | }, (err, response, body) => {
58 | if (err || response.statusCode !== 200) return done(new Error('Error confirming password reset'))
59 | send('ui:set', { resetPasswordConfirmSubmitted: true }, done)
60 | })
61 | },
62 | invite: (data, state, send, done) => {
63 | const email = data.email
64 | const password = uuid.v4()
65 | series([
66 | (cb) => send('user:register', { email, password }, cb),
67 | (cb) => send('user:resetPasswordInit', { email }, cb)
68 | ], (err) => {
69 | if (err) return done(new Error('Error inviting user'))
70 | send('ui:set', { inviteSubmitted: true }, done)
71 | })
72 | },
73 | register: (data, state, send, done) => {
74 | const { email, password } = data
75 | const userData = { metadata: { roles: ['agent'] } }
76 | db.signup(email, password, userData, done)
77 | }
78 | }
79 | })
80 |
--------------------------------------------------------------------------------
/test/client/conversations.js:
--------------------------------------------------------------------------------
1 | const test = require('ava')
2 | const extend = require('xtend')
3 | const sortBy = require('lodash/sortby')
4 | require('jsdom-global')()
5 |
6 | const model = require('../../client/models/convos')()
7 | const Layout = require('../../client/views/layout')
8 | const Chat = require('../../client/views/chat')
9 | const View = Layout(Chat)
10 | const messagesFixture = require('../fixtures/formatted/messages.json')
11 | const samplePhone = '+17034524023'
12 |
13 | function generateState (messages, prev = {}) {
14 | return {
15 | convos: extend(model.state, model.reducers.upsert(messages, prev)),
16 | user: {}
17 | }
18 | }
19 |
20 | test('reducers : upsert : group by message id', (t) => {
21 | const state = generateState(messagesFixture)
22 | const keys = Object.keys(state.convos.messages)
23 | t.is(keys.length, 50, 'messages has correct number of keys')
24 | })
25 |
26 | test('pages : chat : group messages by conversation', (t) => {
27 | const state = generateState(messagesFixture)
28 | const send = () => {}
29 | const view = View(state, {}, send)
30 |
31 | const convoLinks = view.querySelector('#conversations').children
32 | t.is(convoLinks.length, 8, 'correct number of conversation links')
33 | })
34 |
35 | test('pages : chat : show active conversation messages', (t) => {
36 | const state = generateState(messagesFixture)
37 | state.params = { phone: samplePhone }
38 | const send = () => {}
39 | const view = View(state, {}, send)
40 |
41 | const messageItems = view.querySelector('#messages').children
42 | t.is(messageItems.length, 4, 'correct number of messages')
43 | })
44 |
45 | test('pages : chat : new message merged into existing list', (t) => {
46 | const stateBefore = generateState(messagesFixture)
47 | stateBefore.params = { phone: samplePhone }
48 | const send = () => {}
49 | const viewBefore = View(stateBefore, {}, send)
50 |
51 | const numConvosBefore = viewBefore.querySelector('#conversations').children.length
52 | const numMessagesBefore = viewBefore.querySelector('#messages').children.length
53 |
54 | const newMsg = {
55 | _id: '1234',
56 | date: (new Date()).toISOString(),
57 | from: samplePhone,
58 | body: 'Hello, world',
59 | direction: 'inbound'
60 | }
61 |
62 | const stateAfter = generateState([newMsg], stateBefore.convos)
63 | stateAfter.params = { phone: samplePhone }
64 | const viewAfter = View(stateAfter, {}, send)
65 | const numConvosAfter = viewAfter.querySelector('#conversations').children.length
66 | const numMessagesAfter = viewAfter.querySelector('#messages').children.length
67 |
68 | t.is(numConvosAfter, numConvosBefore, 'same number of convos')
69 | t.is(numMessagesAfter, numMessagesBefore + 1, 'one more message')
70 | })
71 |
72 | test('pages : chat : sort messages list', (t) => {
73 | const state = generateState(messagesFixture)
74 | state.params = { phone: samplePhone }
75 | const send = () => {}
76 | const view = View(state, {}, send)
77 |
78 | const uiMessages = view.querySelector('#messages').children
79 | const latestUIMessage = uiMessages[uiMessages.length - 1]
80 | t.is(uiMessages.length, 4, 'list has correct number of convos')
81 |
82 | const relevantFixtures = messagesFixture
83 | .filter((msg) => msg.from === samplePhone || msg.to === samplePhone)
84 | const latestRelevantFixture = sortBy(relevantFixtures, 'date').reverse()[0]
85 |
86 | const latestUIMessageBody = latestUIMessage.querySelector('.content p').textContent.trim()
87 | const latestFixtureBody = latestRelevantFixture.body
88 | t.is(latestUIMessageBody, latestFixtureBody, 'last message is most recent')
89 | })
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # enviar
2 |
3 | Real-time, multi-user chat interface for SMS / text messages. Imagine a call center where customers can
4 | send a text message to a single number and have a live chat with any agent(s) available.
5 |
6 | 
7 |
8 | ## Installation
9 | You can run enviar entirely on free hosted platforms: [Heroku](https://heroku.com)
10 | for the application and [Cloudant](https://cloudant.com) for the database. But
11 | you may prefer to run it locally or on your own server.
12 |
13 | ### Account credentials
14 | To use enviar in production, you'll need a [Twilio](http://twilio.com) account
15 | for sending SMS and a [Postmark](http://postmarkapp.com) account for sending
16 | password reset emails. If you're setting enviar up for production, I recommend
17 | creating those ahead of time because you'll need your twilio account's
18 | "Account SID" and "Auth Token", and your postmark account's "server token" as
19 | part of the application install process. If you're not setting up enviar for
20 | production, you can skip this part for now.
21 |
22 | ### Free hosted installation
23 |
24 | #### Database
25 | You can use [Cloudant's](https://cloudant.com) free tier for the database by
26 | setting up an account there. Create a database called `enviar` once you've signed up.
27 |
28 | #### Application
29 | You can run the application on [Heroku's](https://heroku.com) free tier.
30 |
31 | [](https://heroku.com/deploy)
32 |
33 | Fill in the parameters from the accounts you've setup, including your Cloudant
34 | account in the `COUCHDB` settings.
35 |
36 | You should be able to view your application at `https://.herokuapp.com`
37 | and login using your Cloudant credentials.
38 |
39 | ### Local / self-hosted
40 |
41 | #### Database
42 | This application uses CouchDB to store messages. Follow their [install docs](http://docs.couchdb.org/en/1.6.1/install/index.html)
43 | to run CouchDB locally.
44 |
45 | By default, CouchDB considers everyone an admin, which is known as "Admin Party."
46 | Disable this by going to the control panel (usually at `http://localhost:5984/_utils`)
47 | and [disabling it](http://i.imgur.com/CNtlaBK.png) (image credit @nolanlawson).
48 | In doing so, you'll create your admin account with a password.
49 |
50 | #### Application
51 | 1. Clone this repo using `git clone https://github.com/timwis/enviar.git`
52 | 2. Install node dependencies via `npm install`
53 | 3. Copy `.env.sample` to `.env`
54 | 4. Fill in your the `COUCHDB_HOST` in `.env` (ie. `http://localhost:5984`), along
55 | with the `COUCHDB_USER` and `COUCHDB_PASS` you setup. The other variables are only
56 | required in production mode.
57 | 5. Enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
58 | by running `npm run cors` from the terminal
59 |
60 | ## Usage
61 |
62 | ### Development mode
63 | 1. Run the server using `NODE_ENV=development npm start`
64 | 2. Access the server at `http://localhost:3000`
65 |
66 | You can simulate sending messages using the interface. To simulate receiving a message,
67 | send a `POST` request to `http://localhost:3000/api/inbound`.
68 |
69 | Sample inbound `POST` request: (note the `+` in the phone number is encoded as `%2B`)
70 |
71 | ```bash
72 | curl -X POST -d 'SmsSid=123456&From=%2B12597150948&Body=hello' http://localhost:3000/api/inbound
73 | ```
74 |
75 | ### Production mode
76 | 1. Make sure all the credentials are filled out in `.env`
77 | 2. Run the server using `npm start`
78 | 3. Point twilio's incoming message webhook to `http:///api/inbound` (check out [ngrok](https://ngrok.com/) to expose your localhost)
79 |
80 | Access the server at `http://:3000` (override port using `PORT` environment variable)
81 |
--------------------------------------------------------------------------------
/client/models/convos.js:
--------------------------------------------------------------------------------
1 | const keyBy = require('lodash/keyBy')
2 | const shortid = require('shortid')
3 | const series = require('run-series')
4 | const extend = require('xtend')
5 |
6 | const { localStorageWrapper, hasAgentAccess } = require('../util')
7 |
8 | module.exports = (db, initialUserState) => ({
9 | namespace: 'convos',
10 | state: {
11 | messages: {},
12 | lastRead: {}, // { '+12151231234': '2016-08-22T00:01:02Z' }
13 | isAdding: false
14 | },
15 | reducers: {
16 | upsert: (messages, state) => {
17 | const keyedMessages = keyBy(messages, '_id')
18 | const newMessages = extend(state.messages, keyedMessages)
19 | return { messages: newMessages }
20 | },
21 | setAdding: (isAdding, state) => {
22 | return { isAdding }
23 | },
24 | setLastRead: (newLastRead, state) => {
25 | // Actual updates are performed in the effect and passed to this reducer
26 | return { lastRead: newLastRead || {} }
27 | },
28 | reset: (data, state) => {
29 | return module.exports().state
30 | }
31 | },
32 | effects: {
33 | fetch: (data, state, send, done) => {
34 | db.allDocs({ include_docs: true, startkey: 'msg-' }, (err, result) => {
35 | if (err) return console.error('Error fetching docs', err)
36 | const messages = result.rows.map((row) => row.doc)
37 | send('convos:upsert', messages, done)
38 | })
39 | },
40 | sendOutbound: (data, state, send, done) => {
41 | data._id = `msg-${Date.now()}-${shortid.generate()}`
42 | data.direction = 'outbound'
43 | db.put(data, (err, response) => {
44 | if (err) return done(new Error('Error posting doc'))
45 | done() // if outbound is successful, new message is emitted & received via subscription
46 | })
47 | },
48 | receiveInbound: (message, state, send, done) => {
49 | const operations = []
50 | const isNew = !(message._id in state.messages)
51 |
52 | if (!document.hasFocus() && message.direction === 'inbound' && isNew) {
53 | operations.push((cb) => send('pushNotification', message, cb))
54 | }
55 |
56 | operations.push((cb) => send('convos:upsert', [message], cb))
57 | series(operations, done)
58 | },
59 | addConversation: (phone, state, send, done) => {
60 | series([
61 | (cb) => send('convos:setAdding', false, cb),
62 | (cb) => {
63 | const path = '/' + phone
64 | send('redirect', path, cb)
65 | }
66 | ])
67 | },
68 | saveLastRead: (data, state, send, done) => {
69 | const { phone, date } = data
70 | if (!state.lastRead[phone] || date > state.lastRead[phone]) {
71 | const newLastRead = extend(state.lastRead, { [phone]: date })
72 | localStorageWrapper('lastRead', newLastRead, (err) => {
73 | if (err) return done(new Error('Error saving last read timestamp to local storage'))
74 | send('convos:setLastRead', newLastRead, done)
75 | })
76 | }
77 | }
78 | },
79 | subscriptions: {
80 | watchInbound: (send, done) => {
81 | if (hasAgentAccess(initialUserState)) {
82 | db.changes({
83 | since: 'now',
84 | live: true,
85 | include_docs: true
86 | }).on('change', (change) => {
87 | if (change.id.substring(0, 4) !== 'msg-') return // filter out design docs
88 | send('convos:receiveInbound', change.doc, done)
89 | })
90 | }
91 | },
92 | getCachedLastRead: (send, done) => {
93 | localStorageWrapper('lastRead', (err, result) => {
94 | if (err) return done(new Error('Error retrieving last read timestamps from local storage'))
95 | send('convos:setLastRead', result, done)
96 | })
97 | }
98 | }
99 | })
100 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('dotenv').config({silent: true})
3 | const TWILIO_ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID
4 | const TWILIO_AUTH_TOKEN = process.env.TWILIO_AUTH_TOKEN
5 | const TWILIO_PHONE = process.env.TWILIO_PHONE
6 | const PORT = process.env.PORT || 3000
7 | const DEV = process.env.NODE_ENV === 'development'
8 | const APP_TITLE = process.env.APP_TITLE || require('../package.json').name
9 | const COUCHDB_HOST = process.env.COUCHDB_HOST
10 | const COUCHDB_USER = process.env.COUCHDB_USER
11 | const COUCHDB_PASS = process.env.COUCHDB_PASS
12 | const POSTMARK_SERVER_TOKEN = process.env.POSTMARK_SERVER_TOKEN
13 |
14 | const http = require('http')
15 | const assert = require('assert')
16 | const nano = require('nano')
17 | const serverRouter = require('server-router')
18 | const bankai = require('bankai')
19 | const browserify = require('browserify')
20 | const path = require('path')
21 | const jsonBody = require('body/json')
22 | const formBody = require('body/form')
23 | const bootstrap = require('couchdb-bootstrap')
24 |
25 | const fetchMessages = require('./fetch-messages')
26 | const receiveInbound = require('./receive-inbound')
27 | const followOutbound = require('./follow-outbound')
28 | const initReset = require('./reset-password').initReset
29 | const confirmReset = require('./reset-password').confirmReset
30 | const addAuthToUrl = require('./util').addAuthToUrl
31 | const parseBody = require('./util').parseBody
32 | const pipeToResponse = require('./util').pipeToResponse
33 |
34 | // Setup twilio and postmark client (or stubs)
35 | let twilio, emailClient
36 | if (DEV) {
37 | twilio = require('../test/helpers/twilio')
38 | const postmark = require('../test/helpers/postmark')
39 | emailClient = new postmark.Client()
40 | } else {
41 | assert(TWILIO_ACCOUNT_SID, 'TWILIO_ACCOUNT_SID environment variable is not defined')
42 | assert(TWILIO_AUTH_TOKEN, 'TWILIO_AUTH_TOKEN environment variable is not defined')
43 | assert(TWILIO_PHONE, 'TWILIO_PHONE environment variable is not defined')
44 | twilio = require('twilio')(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
45 |
46 | assert(POSTMARK_SERVER_TOKEN, 'POSTMARK_SERVER_TOKEN environment variable is not defined')
47 | const postmark = require('postmark')
48 | emailClient = new postmark.Client(POSTMARK_SERVER_TOKEN)
49 | }
50 |
51 | // Setup CouchDB
52 | assert(COUCHDB_HOST, 'COUCHDB_HOST environment variable is not defined')
53 | const authUrl = COUCHDB_USER ? addAuthToUrl(COUCHDB_HOST, COUCHDB_USER, COUCHDB_PASS) : COUCHDB_HOST
54 | const configPath = path.join(__dirname, '../database/couchdb')
55 |
56 | bootstrap(authUrl, configPath, { index: true }, (err, body) => {
57 | if (err) return console.error('Error bootstrapping database', err)
58 | console.log('Database bootstrapped', body)
59 |
60 | const messagesDB = nano(authUrl).use('enviar')
61 | const usersDB = nano(authUrl).use('_users')
62 | fetchMessages(messagesDB, twilio)
63 | followOutbound(messagesDB, twilio, TWILIO_PHONE)
64 |
65 | // Setup HTTP server
66 | const router = serverRouter()
67 | const assets = bankai()
68 |
69 | const htmlHandler = assets.html({ title: APP_TITLE, entry: '/bundle.js', css: '/bundle.css' })
70 | router.on('/', pipeToResponse(htmlHandler))
71 |
72 | const cssHandler = assets.css()
73 | router.on('/bundle.css', pipeToResponse(cssHandler))
74 |
75 | const jsPath = path.resolve(__dirname, '../client/index.js')
76 | const jsHandler = assets.js(browserify, jsPath, { transform: 'envify', debug: DEV })
77 | router.on('/bundle.js', pipeToResponse(jsHandler))
78 |
79 | const receiveInboundHandler = receiveInbound(messagesDB)
80 | router.on('/api/inbound', {
81 | post: parseBody(formBody, receiveInboundHandler)
82 | })
83 |
84 | const initResetHandler = initReset(usersDB, emailClient)
85 | router.on('/api/reset-password-init', {
86 | post: parseBody(jsonBody, initResetHandler)
87 | })
88 |
89 | const confirmResetHandler = confirmReset(usersDB)
90 | router.on('/api/reset-password-confirm', {
91 | post: parseBody(jsonBody, confirmResetHandler)
92 | })
93 |
94 | http.createServer(router).listen(PORT, () => console.log('Listening on port', PORT))
95 | })
96 |
--------------------------------------------------------------------------------
/test/server/reset-password.js:
--------------------------------------------------------------------------------
1 | const test = require('ava')
2 | const td = require('testdouble')
3 |
4 | const { initReset, confirmReset } = require('../../server/reset-password')
5 | const { matchWithEmail, matchWithToken } = require('../fixtures/users/generator')
6 |
7 | test('reset password : initReset : if no user found, return 200 and send not found email', (t) => {
8 | const email = 'foo@bar.com'
9 | const db = stubDB('byEmail', email) // no sampleData will throw error
10 | const emailClient = stubEmail()
11 | const res = stubResponse()
12 | const req = { body: { email } }
13 |
14 | const initResetHandler = initReset(db, emailClient)
15 | initResetHandler(req, res)
16 |
17 | const emailConfigMatch = { Subject: 'Attempted password reset for enviar' }
18 | td.verify(emailClient.sendEmail(td.matchers.contains(emailConfigMatch), td.matchers.isA(Function)))
19 |
20 | t.is(res.statusCode, 200, 'status code is 200')
21 | })
22 |
23 | test('reset password : initReset : if user found, save reset token & email them', (t) => {
24 | const email = 'foo@bar.com'
25 | const sampleData = matchWithEmail(email)
26 | const db = stubDB('byEmail', email, sampleData)
27 | const emailClient = stubEmail()
28 | const res = stubResponse()
29 | const req = { body: { email } }
30 |
31 | const initResetHandler = initReset(db, emailClient)
32 | initResetHandler(req, res)
33 |
34 | td.verify(db.insert(td.matchers.isA(Object), td.matchers.isA(Function)))
35 |
36 | const emailConfigMatch = { Subject: 'Password reset for enviar' }
37 | td.verify(emailClient.sendEmail(td.matchers.contains(emailConfigMatch), td.matchers.isA(Function)))
38 |
39 | t.is(res.statusCode, 200, 'status code is 200')
40 | })
41 |
42 | test('reset password : confirmReset : if no user found, return 404', (t) => {
43 | const token = 'a9s8df7a98sd'
44 | const password = 'foobar'
45 | const sampleData = { rows: [] }
46 | const db = stubDB('byResetToken', token, sampleData)
47 | const res = stubResponse()
48 | const req = { body: { token, password } }
49 |
50 | const confirmResetHandler = confirmReset(db)
51 | confirmResetHandler(req, res)
52 | t.is(res.statusCode, 404, 'status code is 404')
53 | })
54 |
55 | test('reset password : confirmReset : if token expired, return 404', (t) => {
56 | const token = 'a9s8df7a98sd'
57 | const password = 'foobar'
58 | const email = 'foo@bar.com'
59 | const sampleData = matchWithToken(email, token, 31)
60 | const db = stubDB('byResetToken', token, sampleData)
61 | const res = stubResponse()
62 | const req = { body: { token, password } }
63 |
64 | const confirmResetHandler = confirmReset(db)
65 | confirmResetHandler(req, res)
66 | t.is(res.statusCode, 404, 'status code is 404')
67 | })
68 |
69 | test('reset password : confirmReset : if valid token, reset password and delete token', (t) => {
70 | const token = 'a9s8df7a98sd'
71 | const password = 'foobar'
72 | const email = 'foo@bar.com'
73 | const sampleData = matchWithToken(email, token, 29)
74 | const db = stubDB('byResetToken', token, sampleData)
75 | const res = stubResponse()
76 | const req = { body: { token, password } }
77 |
78 | const confirmResetHandler = confirmReset(db)
79 | confirmResetHandler(req, res)
80 |
81 | td.verify(db.insert(td.matchers.argThat((n) => n.password === password && !n.metadata.resetToken), td.matchers.isA(Function)))
82 |
83 | t.is(res.statusCode, 200, 'status code is 200')
84 | })
85 |
86 | function stubDB (viewName, lookup, response) {
87 | const db = td.object({
88 | get: () => {},
89 | view: () => {},
90 | insert: () => {}
91 | })
92 | const key = 'org.couchdb.user:' + lookup
93 | const error = response ? null : new Error('Not found')
94 | const viewOpts = { keys: [lookup], include_docs: true }
95 | td.when(db.view('users', viewName, viewOpts)).thenCallback(error, response)
96 | td.when(db.get(key)).thenCallback(error, response)
97 | td.when(db.insert(td.matchers.isA(Object))).thenCallback()
98 | return db
99 | }
100 |
101 | function stubEmail () {
102 | const emailClient = td.object({
103 | sendEmail: () => {}
104 | })
105 | td.when(emailClient.sendEmail(td.matchers.isA(Object))).thenCallback()
106 | return emailClient
107 | }
108 |
109 | function stubResponse () {
110 | return td.object({
111 | statusCode: null,
112 | setHeader: () => {},
113 | end: () => {}
114 | })
115 | }
116 |
--------------------------------------------------------------------------------
/server/reset-password.js:
--------------------------------------------------------------------------------
1 | const uuid = require('uuid')
2 | const extend = require('xtend')
3 | const path = require('path')
4 | const stripIndent = require('common-tags').stripIndent
5 |
6 | const BASE_URL = process.env.BASE_URL || ''
7 | const FROM_EMAIL = process.env.FROM_EMAIL || 'noreply@noreply.com'
8 |
9 | const respond = require('./util').respond
10 |
11 | const TOKEN_LIFESPAN = 30 // minutes
12 |
13 | exports.initReset = initReset
14 | exports.confirmReset = confirmReset
15 |
16 | function initReset (db, emailClient) {
17 | return function initResetHandler (req, res) {
18 | const email = req.body.email
19 | if (!email) return respond(res, 400, 'No email provided')
20 |
21 | // Find user document
22 | const docId = 'org.couchdb.user:' + email
23 | db.get(docId, (err, doc) => {
24 | if (err) {
25 | // No user found
26 | const emailConfig = {
27 | From: FROM_EMAIL,
28 | To: email,
29 | Subject: 'Attempted password reset for enviar',
30 | TextBody: notFoundTemplate()
31 | }
32 |
33 | // Email "not found" notice to user
34 | emailClient.sendEmail(emailConfig, (err, result) => {
35 | if (err) return respond(res, 500, 'Error sending reset email')
36 |
37 | res.statusCode = 200
38 | res.end()
39 | })
40 | } else {
41 | // Found user document
42 | const token = uuid.v4()
43 | const newUserDoc = extend(doc, {
44 | metadata: {
45 | resetToken: {
46 | created: Date.now(),
47 | token
48 | }
49 | }
50 | })
51 |
52 | // Add reset token to user document
53 | db.insert(newUserDoc, (err, body) => {
54 | if (err) return respond(res, 500, 'Error adding reset token to user document')
55 |
56 | const emailConfig = {
57 | From: FROM_EMAIL,
58 | To: email,
59 | Subject: 'Password reset for enviar',
60 | TextBody: resetEmailTemplate(token)
61 | }
62 |
63 | // Email reset token to user
64 | emailClient.sendEmail(emailConfig, (err, result) => {
65 | if (err) return respond(res, 500, 'Error sending reset email')
66 |
67 | res.statusCode = 200
68 | res.end()
69 | })
70 | })
71 | }
72 | })
73 | }
74 | }
75 |
76 | function confirmReset (db) {
77 | return function confirmResetHandler (req, res) {
78 | const token = req.body.token
79 | const password = req.body.password
80 | if (!token || !password) return respond(res, 400, 'Token or password missing')
81 |
82 | const viewOpts = { keys: [token], include_docs: true }
83 | db.view('users', 'byResetToken', viewOpts, (err, body) => {
84 | if (err) return respond(res, 500, 'Error fetching user document')
85 | if (!body.rows.length || isExpired(body.rows[0].value)) {
86 | return respond(res, 404, 'Token not found or token is inactive')
87 | }
88 |
89 | // User document found with that token
90 | const newUserDoc = extend(body.rows[0].doc)
91 | newUserDoc.password = password
92 | delete newUserDoc.metadata.resetToken
93 |
94 | // Update password and remove reset token from user document
95 | db.insert(newUserDoc, (err, body) => {
96 | if (err) respond(res, 500, 'Error saving user document')
97 |
98 | res.statusCode = 200
99 | res.end()
100 | })
101 | })
102 | }
103 | }
104 |
105 | function isExpired (timestamp) {
106 | return (Date.now() - timestamp) / 1000 / 60 > TOKEN_LIFESPAN
107 | }
108 |
109 | function resetEmailTemplate (token) {
110 | return stripIndent`
111 | Hello,
112 |
113 | Follow this link to reset your enviar password for your enviar account.
114 |
115 | ${path.join(BASE_URL, '/reset-password/', token)}
116 |
117 | If you didn’t ask to reset your password, you can ignore this email.
118 |
119 | Thanks,
120 |
121 | The enviar team
122 | `
123 | }
124 |
125 | function notFoundTemplate () {
126 | return stripIndent`
127 | You (or someone else) entered this email address when trying to change the password of an enviar account.
128 |
129 | However, this email address is not in our database of registered users and therefore the attempted password change has failed.
130 |
131 | If you have an enviar account and were expecting this email, please try again using the email address you gave when opening your account.
132 |
133 | If you do not have an enviar account, please ignore this email.
134 |
135 | For information about enviar, visit ${BASE_URL}.
136 |
137 | Kind regards,
138 |
139 | The enviar team
140 | `
141 | }
142 |
--------------------------------------------------------------------------------
/test/fixtures/formatted/messages.json:
--------------------------------------------------------------------------------
1 | [{
2 | "_id": "SMb060641d912135f1999f57f167290d64",
3 | "date": "2015-05-17T00:35:37.000Z",
4 | "from": "+15851427131",
5 | "to": "+11582979687",
6 | "body": "Minim labore eiusmod magna commodo.",
7 | "status": "delivered",
8 | "direction": "outbound-reply"
9 | }, {
10 | "_id": "SM1ccdfc8904226cd2b1c789b8808d3cc8",
11 | "date": "2015-05-17T00:35:29.000Z",
12 | "from": "+11582979687",
13 | "to": "+15851427131",
14 | "body": "Fugiat pro_ident ut quis exercitation labore non.",
15 | "status": "received",
16 | "direction": "inbound"
17 | }, {
18 | "_id": "SMcc0d9da27dbb7d3ed8490e724b511579",
19 | "date": "2015-02-27T00:56:39.000Z",
20 | "from": "+15851427131",
21 | "to": "+11582979687",
22 | "body": "Aliqua elit qui ad non occaecat excepteur ea sunt excepteur ut adipisicing.",
23 | "status": "delivered",
24 | "direction": "outbound-reply"
25 | }, {
26 | "_id": "SM062894402211815157db9c21ffebdd02",
27 | "date": "2015-02-27T00:56:34.000Z",
28 | "from": "+11582979687",
29 | "to": "+15851427131",
30 | "body": "Duis nostrud amet nostrud aliqua amet.",
31 | "status": "received",
32 | "direction": "inbound"
33 | }, {
34 | "_id": "SM450e9e96877e87646fc02e814cd20b03",
35 | "date": "2015-02-26T19:29:51.000Z",
36 | "from": "+15851427131",
37 | "to": "+11085432310",
38 | "body": "Eu duis ut adipisicing cillum ut.",
39 | "status": "delivered",
40 | "direction": "outbound-reply"
41 | }, {
42 | "_id": "SM99f72c308caf8f635887983da047c3f3",
43 | "date": "2015-02-26T19:29:50.000Z",
44 | "from": "+11085432310",
45 | "to": "+15851427131",
46 | "body": "Fugiat cillum tempor irure nulla pariatur nisi adipisicing in tempor quis quis labore qui.",
47 | "status": "received",
48 | "direction": "inbound"
49 | }, {
50 | "_id": "SM910ad3c6eae9b233d581ed4d634449fa",
51 | "date": "2015-02-26T19:29:14.000Z",
52 | "from": "+15851427131",
53 | "to": "+11085432310",
54 | "body": "Anim in commodo sit anim officia magna ipsum.",
55 | "status": "delivered",
56 | "direction": "outbound-reply"
57 | }, {
58 | "_id": "SMddf8858e4ebb03d2deadcf7f0f93ae10",
59 | "date": "2015-02-26T19:29:13.000Z",
60 | "from": "+11085432310",
61 | "to": "+15851427131",
62 | "body": "Nostrud labore eiusmod voluptate tempor non amet amet ea aute reprehenderit enim et nulla.",
63 | "status": "received",
64 | "direction": "inbound"
65 | }, {
66 | "_id": "SMb72ff3fbf7906849883558b7ea2acfdd",
67 | "date": "2015-02-26T19:28:35.000Z",
68 | "from": "+15851427131",
69 | "to": "+13088632703",
70 | "body": "Aliqua consectetur _id commodo nisi sunt voluptate aute eu fugiat laboris reprehenderit dolor.",
71 | "status": "delivered",
72 | "direction": "outbound-reply"
73 | }, {
74 | "_id": "SMe066c282472840f11d4881e0e317a023",
75 | "date": "2015-02-26T19:28:35.000Z",
76 | "from": "+13088632703",
77 | "to": "+15851427131",
78 | "body": "Ut dolore quis fugiat _id.",
79 | "status": "received",
80 | "direction": "inbound"
81 | }, {
82 | "_id": "SMed30d7fa8812494516f2d54240f6a78e",
83 | "date": "2015-02-26T19:28:34.000Z",
84 | "from": "+15851427131",
85 | "to": "+17034524023",
86 | "body": "Fugiat dolor aute in irure mollit occaecat elit.",
87 | "status": "delivered",
88 | "direction": "outbound-reply"
89 | }, {
90 | "_id": "SMc97746bc5f4435463df7298233558106",
91 | "date": "2015-02-26T19:28:33.000Z",
92 | "from": "+17034524023",
93 | "to": "+15851427131",
94 | "body": "Culpa adipisicing occaecat occaecat ipsum _id commodo quis veniam.",
95 | "status": "received",
96 | "direction": "inbound"
97 | }, {
98 | "_id": "SM6c69b2aedf3211dbfa483ec53403a82c",
99 | "date": "2015-02-26T19:28:18.000Z",
100 | "from": "+15851427131",
101 | "to": "+18714198645",
102 | "body": "Anim esse ipsum labore ea commodo aute do ea est velit occaecat ipsum reprehenderit.",
103 | "status": "delivered",
104 | "direction": "outbound-reply"
105 | }, {
106 | "_id": "SMa1ad539d954f11796be6855158055618",
107 | "date": "2015-02-26T19:28:18.000Z",
108 | "from": "+18714198645",
109 | "to": "+15851427131",
110 | "body": "Ut magna nostrud laborum ipsum excepteur.",
111 | "status": "received",
112 | "direction": "inbound"
113 | }, {
114 | "_id": "SM903010630adb89d271aa170ae634f210",
115 | "date": "2015-02-26T19:27:41.000Z",
116 | "from": "+15851427131",
117 | "to": "+11085432310",
118 | "body": "Fugiat mollit consectetur qui aute reprehenderit labore eu.",
119 | "status": "delivered",
120 | "direction": "outbound-reply"
121 | }, {
122 | "_id": "SMf3d0fdcc7852a3d47f2d6361064ea5cb",
123 | "date": "2015-02-26T19:27:40.000Z",
124 | "from": "+11085432310",
125 | "to": "+15851427131",
126 | "body": "Non _id commodo ullamco consectetur deserunt.",
127 | "status": "received",
128 | "direction": "inbound"
129 | }, {
130 | "_id": "SMc6114c85b88fe9d5fce514a3d6fd0fe5",
131 | "date": "2015-02-26T19:27:24.000Z",
132 | "from": "+15851427131",
133 | "to": "+13088632703",
134 | "body": "Aute commodo Lorem magna consequat reprehenderit _id elit exercitation et reprehenderit.",
135 | "status": "delivered",
136 | "direction": "outbound-reply"
137 | }, {
138 | "_id": "SM3366851272a474fab8d8cf00ea4250e3",
139 | "date": "2015-02-26T19:27:24.000Z",
140 | "from": "+13088632703",
141 | "to": "+15851427131",
142 | "body": "Amet est inc_id_idunt aute fugiat _id adipisicing.",
143 | "status": "received",
144 | "direction": "inbound"
145 | }, {
146 | "_id": "SM1cb9cc90ba3a3cc5c01b2d9bc20f9720",
147 | "date": "2015-02-26T19:18:34.000Z",
148 | "from": "+15851427131",
149 | "to": "+11582979687",
150 | "body": "Pro_ident qui magna cup_idatat eiusmod veniam laborum Lorem inc_id_idunt sint.",
151 | "status": "delivered",
152 | "direction": "outbound-reply"
153 | }, {
154 | "_id": "SM419db52c4b77f4648c80ad9bd3fa52f3",
155 | "date": "2015-02-26T19:18:33.000Z",
156 | "from": "+11582979687",
157 | "to": "+15851427131",
158 | "body": "Excepteur ut magna ea laboris deserunt ex.",
159 | "status": "received",
160 | "direction": "inbound"
161 | }, {
162 | "_id": "SM80fcd6c1daf3f7aa48e9d1e2671d4745",
163 | "date": "2015-02-26T19:17:56.000Z",
164 | "from": "+15851427131",
165 | "to": "+11582979687",
166 | "body": "Elit Lorem pariatur dolore sint non culpa occaecat ipsum do sunt irure cillum eu ad.",
167 | "status": "delivered",
168 | "direction": "outbound-reply"
169 | }, {
170 | "_id": "SMe6a7a63327472326de1d49f490be7774",
171 | "date": "2015-02-26T19:17:55.000Z",
172 | "from": "+11582979687",
173 | "to": "+15851427131",
174 | "body": "Laboris irure magna ut nostrud.",
175 | "status": "received",
176 | "direction": "inbound"
177 | }, {
178 | "_id": "SM87312304f5f896e147e93300be2466b2",
179 | "date": "2015-02-26T19:17:46.000Z",
180 | "from": "+15851427131",
181 | "to": "+13088632703",
182 | "body": "Quis exercitation dolore occaecat aute qui fugiat labore excepteur consequat est officia.",
183 | "status": "delivered",
184 | "direction": "outbound-reply"
185 | }, {
186 | "_id": "SM2a6b78fb2f4c1cf301986e335c0ff035",
187 | "date": "2015-02-26T19:17:46.000Z",
188 | "from": "+13088632703",
189 | "to": "+15851427131",
190 | "body": "Et eiusmod laboris esse labore anim commodo nostrud.",
191 | "status": "received",
192 | "direction": "inbound"
193 | }, {
194 | "_id": "SM8ce08e0c2ee6b2cbd354327dcc17234e",
195 | "date": "2015-02-26T19:17:32.000Z",
196 | "from": "+15851427131",
197 | "to": "+11582979687",
198 | "body": "Lorem deserunt elit veniam Lorem fugiat nulla cup_idatat sint pariatur elit elit.",
199 | "status": "delivered",
200 | "direction": "outbound-reply"
201 | }, {
202 | "_id": "SMc46c42888de4fec754d510b184037248",
203 | "date": "2015-02-26T19:17:28.000Z",
204 | "from": "+11582979687",
205 | "to": "+15851427131",
206 | "body": "Dolore est sint tempor inc_id_idunt nisi anim sit.",
207 | "status": "received",
208 | "direction": "inbound"
209 | }, {
210 | "_id": "SM61f5b518dd5d69c58a5cc6ebdead4554",
211 | "date": "2015-02-26T19:17:16.000Z",
212 | "from": "+15851427131",
213 | "to": "+11582979687",
214 | "body": "Non adipisicing deserunt Lorem deserunt ex cillum qui ipsum ullamco inc_id_idunt est laboris cup_idatat excepteur.",
215 | "status": "delivered",
216 | "direction": "outbound-reply"
217 | }, {
218 | "_id": "SM7a17e1dcaec3519fad5ae166b8cc3393",
219 | "date": "2015-02-26T19:17:09.000Z",
220 | "from": "+11582979687",
221 | "to": "+15851427131",
222 | "body": "Tempor voluptate sint commodo deserunt laboris sint aute dolore officia.",
223 | "status": "received",
224 | "direction": "inbound"
225 | }, {
226 | "_id": "SM7c31f6898deec1e444cfdca334f816e7",
227 | "date": "2015-02-26T19:16:01.000Z",
228 | "from": "+15851427131",
229 | "to": "+11582979687",
230 | "body": "Eiusmod ea mollit occaecat magna.",
231 | "status": "delivered",
232 | "direction": "outbound-reply"
233 | }, {
234 | "_id": "SMe91e716bd9ac85ca825884236707d94a",
235 | "date": "2015-02-26T19:16:00.000Z",
236 | "from": "+11582979687",
237 | "to": "+15851427131",
238 | "body": "Laboris enim exercitation ex qui.",
239 | "status": "received",
240 | "direction": "inbound"
241 | }, {
242 | "_id": "SMf8ded2e6b2282623878191196eb763a2",
243 | "date": "2015-02-26T19:15:34.000Z",
244 | "from": "+15851427131",
245 | "to": "+11085432310",
246 | "body": "Irure cup_idatat et consequat elit.",
247 | "status": "delivered",
248 | "direction": "outbound-reply"
249 | }, {
250 | "_id": "SMfae68bb540e8fa21ebf0f3d05b76ae23",
251 | "date": "2015-02-26T19:15:33.000Z",
252 | "from": "+11085432310",
253 | "to": "+15851427131",
254 | "body": "Sunt ea est aliquip pariatur inc_id_idunt labore ut.",
255 | "status": "received",
256 | "direction": "inbound"
257 | }, {
258 | "_id": "SMce1e84e8dfdfc5ce8a6f23eaf02d4723",
259 | "date": "2015-02-26T19:13:19.000Z",
260 | "from": "+15851427131",
261 | "to": "+13088632703",
262 | "body": "Veniam quis eiusmod pariatur fugiat ad elit ullamco irure consectetur.",
263 | "status": "delivered",
264 | "direction": "outbound-reply"
265 | }, {
266 | "_id": "SM2ddf566d4891bfce2920c67be3ea0c1e",
267 | "date": "2015-02-26T19:13:18.000Z",
268 | "from": "+13088632703",
269 | "to": "+15851427131",
270 | "body": "Cup_idatat pariatur dolore esse pro_ident amet _id aliqua eiusmod minim adipisicing Lorem nulla Lorem Lorem.",
271 | "status": "received",
272 | "direction": "inbound"
273 | }, {
274 | "_id": "SM28be53db114f58075510c6ebd8722a4f",
275 | "date": "2015-02-09T23:10:34.000Z",
276 | "from": "+15851427131",
277 | "to": "+12597150948",
278 | "body": "Cup_idatat ut velit laboris laborum deserunt inc_id_idunt cup_idatat.",
279 | "status": "delivered",
280 | "direction": "outbound-reply"
281 | }, {
282 | "_id": "SM8d8f44cf6205a27a87c9aab0d57453d7",
283 | "date": "2015-02-09T23:10:33.000Z",
284 | "from": "+12597150948",
285 | "to": "+15851427131",
286 | "body": "Exercitation est officia tempor magna amet laborum ad aliqua ipsum.",
287 | "status": "received",
288 | "direction": "inbound"
289 | }, {
290 | "_id": "SMa4008eec5726061f3d58eb1f989f2497",
291 | "date": "2015-02-09T23:09:46.000Z",
292 | "from": "+15851427131",
293 | "to": "+12597150948",
294 | "body": "Velit reprehenderit magna sit nostrud eu nulla nostrud officia est sint nostrud eu.",
295 | "status": "delivered",
296 | "direction": "outbound-reply"
297 | }, {
298 | "_id": "SM6ff133e507fe4c1350dc2b9c2d627f05",
299 | "date": "2015-02-09T23:09:38.000Z",
300 | "from": "+12597150948",
301 | "to": "+15851427131",
302 | "body": "Non elit deserunt velit cup_idatat magna est cillum.",
303 | "status": "received",
304 | "direction": "inbound"
305 | }, {
306 | "_id": "SMa15bc0445c7e3a15afd7365b66932a6b",
307 | "date": "2015-02-09T20:12:22.000Z",
308 | "from": "+15851427131",
309 | "to": "+17034524023",
310 | "body": "Excepteur eu ea fugiat inc_id_idunt.",
311 | "status": "delivered",
312 | "direction": "outbound-reply"
313 | }, {
314 | "_id": "SM36ba30e62c727a91f54e7d69dca955ca",
315 | "date": "2015-02-09T20:12:19.000Z",
316 | "from": "+17034524023",
317 | "to": "+15851427131",
318 | "body": "Do veniam fugiat irure culpa voluptate ea sunt nisi irure.",
319 | "status": "received",
320 | "direction": "inbound"
321 | }, {
322 | "_id": "SM03278d5d477b8511c90038bb271b6c43",
323 | "date": "2015-02-09T20:04:05.000Z",
324 | "from": "+15851427131",
325 | "to": "+11628737261",
326 | "body": "Ut consequat esse et nisi esse.",
327 | "status": "delivered",
328 | "direction": "outbound-reply"
329 | }, {
330 | "_id": "SM2cafefdd022c5a8ace7c828a21e2b53d",
331 | "date": "2015-02-09T20:04:05.000Z",
332 | "from": "+11628737261",
333 | "to": "+15851427131",
334 | "body": "_id tempor elit esse esse sint ullamco Lorem in est.",
335 | "status": "received",
336 | "direction": "inbound"
337 | }, {
338 | "_id": "SMef0e90c171ee7e81ac53c80876537cd2",
339 | "date": "2015-02-09T20:03:55.000Z",
340 | "from": "+15851427131",
341 | "to": "+11628737261",
342 | "body": "Exercitation occaecat mollit pro_ident ea cillum cillum cup_idatat voluptate commodo pariatur Lorem sint.",
343 | "status": "delivered",
344 | "direction": "outbound-reply"
345 | }, {
346 | "_id": "SMa7a099d63fde9e54163253a96a162182",
347 | "date": "2015-02-09T20:03:54.000Z",
348 | "from": "+11628737261",
349 | "to": "+15851427131",
350 | "body": "Quis eu do pariatur sunt quis pro_ident non irure amet mollit non culpa cillum aliquip.",
351 | "status": "received",
352 | "direction": "inbound"
353 | }, {
354 | "_id": "SM7eecbad339332a1e0c8eee7553e6e0b9",
355 | "date": "2015-02-09T20:02:15.000Z",
356 | "from": "+15851427131",
357 | "to": "+17668017655",
358 | "body": "Ex aliquip irure dolore pro_ident occaecat adipisicing minim adipisicing commodo adipisicing adipisicing anim.",
359 | "status": "delivered",
360 | "direction": "outbound-reply"
361 | }, {
362 | "_id": "SM517dafa5d096f4343e897b777508bd7a",
363 | "date": "2015-02-09T20:02:14.000Z",
364 | "from": "+17668017655",
365 | "to": "+15851427131",
366 | "body": "Sunt aute pariatur occaecat in cillum eu pro_ident consectetur eiusmod fugiat reprehenderit est et cillum.",
367 | "status": "received",
368 | "direction": "inbound"
369 | }, {
370 | "_id": "SM63d1c66549aef33b2918aaea83a6e869",
371 | "date": "2015-02-09T19:59:25.000Z",
372 | "from": "+15851427131",
373 | "to": "+12597150948",
374 | "body": "Exercitation commodo reprehenderit consequat deserunt veniam commodo irure.",
375 | "status": "delivered",
376 | "direction": "outbound-reply"
377 | }, {
378 | "_id": "SM7d99deee5fbc9162dcac9c705e0b19dc",
379 | "date": "2015-02-09T19:59:24.000Z",
380 | "from": "+12597150948",
381 | "to": "+15851427131",
382 | "body": "Reprehenderit consequat exercitation pro_ident ullamco commodo aute sunt ipsum excepteur.",
383 | "status": "received",
384 | "direction": "inbound"
385 | }, {
386 | "_id": "SM69eaaa68f1a4291986f18adf615fbcf1",
387 | "date": "2015-02-09T19:44:04.000Z",
388 | "from": "+15851427131",
389 | "to": "+13088632703",
390 | "body": "Non _id reprehenderit in culpa cup_idatat laborum cillum ipsum.",
391 | "status": "delivered",
392 | "direction": "outbound-reply"
393 | }, {
394 | "_id": "SM10188adc98e57da4128e4b5318ae4c82",
395 | "date": "2015-02-09T19:44:03.000Z",
396 | "from": "+13088632703",
397 | "to": "+15851427131",
398 | "body": "Amet ullamco Lorem est veniam occaecat ex duis occaecat.",
399 | "status": "received",
400 | "direction": "inbound"
401 | }]
402 |
--------------------------------------------------------------------------------
/test/fixtures/twilio/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "first_page_uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages.json?PageSize=50&Page=0",
3 | "end": 49,
4 | "previous_page_uri": null,
5 | "messages": [
6 | {
7 | "sid": "SMb060641d912135f1999f57f167290d64",
8 | "date_created": "Sun, 17 May 2015 00:35:37 +0000",
9 | "date_updated": "Sun, 17 May 2015 00:35:38 +0000",
10 | "date_sent": "Sun, 17 May 2015 00:35:37 +0000",
11 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
12 | "to": "+11582979687",
13 | "from": "+15851427131",
14 | "messaging_service_sid": null,
15 | "body": "Minim labore eiusmod magna commodo.",
16 | "status": "delivered",
17 | "num_segments": "1",
18 | "num_media": "0",
19 | "direction": "outbound-reply",
20 | "api_version": "2010-04-01",
21 | "price": "-0.00750",
22 | "price_unit": "USD",
23 | "error_code": null,
24 | "error_message": null,
25 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMb060641d912135f1999f57f167290d64",
26 | "subresource_uris": {
27 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMb060641d912135f1999f57f167290d64/Media.json"
28 | },
29 | "dateCreated": "2015-05-17T00:35:37.000Z",
30 | "dateUpdated": "2015-05-17T00:35:38.000Z",
31 | "dateSent": "2015-05-17T00:35:37.000Z",
32 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
33 | "messagingServiceSid": null,
34 | "numSegments": "1",
35 | "numMedia": "0",
36 | "apiVersion": "2010-04-01",
37 | "priceUnit": "USD",
38 | "errorCode": null,
39 | "errorMessage": null,
40 | "subresourceUris": {
41 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMb060641d912135f1999f57f167290d64/Media.json"
42 | }
43 | },
44 | {
45 | "sid": "SM1ccdfc8904226cd2b1c789b8808d3cc8",
46 | "date_created": "Sun, 17 May 2015 00:35:29 +0000",
47 | "date_updated": "Sun, 17 May 2015 00:35:37 +0000",
48 | "date_sent": "Sun, 17 May 2015 00:35:37 +0000",
49 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
50 | "to": "+15851427131",
51 | "from": "+11582979687",
52 | "messaging_service_sid": null,
53 | "body": "Fugiat proident ut quis exercitation labore non.",
54 | "status": "received",
55 | "num_segments": "1",
56 | "num_media": "0",
57 | "direction": "inbound",
58 | "api_version": "2010-04-01",
59 | "price": "-0.00750",
60 | "price_unit": "USD",
61 | "error_code": null,
62 | "error_message": null,
63 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM1ccdfc8904226cd2b1c789b8808d3cc8",
64 | "subresource_uris": {
65 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM1ccdfc8904226cd2b1c789b8808d3cc8/Media.json"
66 | },
67 | "dateCreated": "2015-05-17T00:35:29.000Z",
68 | "dateUpdated": "2015-05-17T00:35:37.000Z",
69 | "dateSent": "2015-05-17T00:35:37.000Z",
70 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
71 | "messagingServiceSid": null,
72 | "numSegments": "1",
73 | "numMedia": "0",
74 | "apiVersion": "2010-04-01",
75 | "priceUnit": "USD",
76 | "errorCode": null,
77 | "errorMessage": null,
78 | "subresourceUris": {
79 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM1ccdfc8904226cd2b1c789b8808d3cc8/Media.json"
80 | }
81 | },
82 | {
83 | "sid": "SMcc0d9da27dbb7d3ed8490e724b511579",
84 | "date_created": "Fri, 27 Feb 2015 00:56:39 +0000",
85 | "date_updated": "Fri, 27 Feb 2015 00:56:39 +0000",
86 | "date_sent": "Fri, 27 Feb 2015 00:56:39 +0000",
87 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
88 | "to": "+11582979687",
89 | "from": "+15851427131",
90 | "messaging_service_sid": null,
91 | "body": "Aliqua elit qui ad non occaecat excepteur ea sunt excepteur ut adipisicing.",
92 | "status": "delivered",
93 | "num_segments": "1",
94 | "num_media": "0",
95 | "direction": "outbound-reply",
96 | "api_version": "2010-04-01",
97 | "price": "-0.00750",
98 | "price_unit": "USD",
99 | "error_code": null,
100 | "error_message": null,
101 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMcc0d9da27dbb7d3ed8490e724b511579",
102 | "subresource_uris": {
103 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMcc0d9da27dbb7d3ed8490e724b511579/Media.json"
104 | },
105 | "dateCreated": "2015-02-27T00:56:39.000Z",
106 | "dateUpdated": "2015-02-27T00:56:39.000Z",
107 | "dateSent": "2015-02-27T00:56:39.000Z",
108 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
109 | "messagingServiceSid": null,
110 | "numSegments": "1",
111 | "numMedia": "0",
112 | "apiVersion": "2010-04-01",
113 | "priceUnit": "USD",
114 | "errorCode": null,
115 | "errorMessage": null,
116 | "subresourceUris": {
117 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMcc0d9da27dbb7d3ed8490e724b511579/Media.json"
118 | }
119 | },
120 | {
121 | "sid": "SM062894402211815157db9c21ffebdd02",
122 | "date_created": "Fri, 27 Feb 2015 00:56:34 +0000",
123 | "date_updated": "Fri, 27 Feb 2015 00:56:39 +0000",
124 | "date_sent": "Fri, 27 Feb 2015 00:56:39 +0000",
125 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
126 | "to": "+15851427131",
127 | "from": "+11582979687",
128 | "messaging_service_sid": null,
129 | "body": "Duis nostrud amet nostrud aliqua amet.",
130 | "status": "received",
131 | "num_segments": "1",
132 | "num_media": "0",
133 | "direction": "inbound",
134 | "api_version": "2010-04-01",
135 | "price": "-0.00750",
136 | "price_unit": "USD",
137 | "error_code": null,
138 | "error_message": null,
139 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM062894402211815157db9c21ffebdd02",
140 | "subresource_uris": {
141 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM062894402211815157db9c21ffebdd02/Media.json"
142 | },
143 | "dateCreated": "2015-02-27T00:56:34.000Z",
144 | "dateUpdated": "2015-02-27T00:56:39.000Z",
145 | "dateSent": "2015-02-27T00:56:39.000Z",
146 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
147 | "messagingServiceSid": null,
148 | "numSegments": "1",
149 | "numMedia": "0",
150 | "apiVersion": "2010-04-01",
151 | "priceUnit": "USD",
152 | "errorCode": null,
153 | "errorMessage": null,
154 | "subresourceUris": {
155 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM062894402211815157db9c21ffebdd02/Media.json"
156 | }
157 | },
158 | {
159 | "sid": "SM450e9e96877e87646fc02e814cd20b03",
160 | "date_created": "Thu, 26 Feb 2015 19:29:51 +0000",
161 | "date_updated": "Thu, 26 Feb 2015 19:29:51 +0000",
162 | "date_sent": "Thu, 26 Feb 2015 19:29:51 +0000",
163 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
164 | "to": "+11085432310",
165 | "from": "+15851427131",
166 | "messaging_service_sid": null,
167 | "body": "Eu duis ut adipisicing cillum ut.",
168 | "status": "delivered",
169 | "num_segments": "1",
170 | "num_media": "0",
171 | "direction": "outbound-reply",
172 | "api_version": "2010-04-01",
173 | "price": "-0.00750",
174 | "price_unit": "USD",
175 | "error_code": null,
176 | "error_message": null,
177 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM450e9e96877e87646fc02e814cd20b03",
178 | "subresource_uris": {
179 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM450e9e96877e87646fc02e814cd20b03/Media.json"
180 | },
181 | "dateCreated": "2015-02-26T19:29:51.000Z",
182 | "dateUpdated": "2015-02-26T19:29:51.000Z",
183 | "dateSent": "2015-02-26T19:29:51.000Z",
184 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
185 | "messagingServiceSid": null,
186 | "numSegments": "1",
187 | "numMedia": "0",
188 | "apiVersion": "2010-04-01",
189 | "priceUnit": "USD",
190 | "errorCode": null,
191 | "errorMessage": null,
192 | "subresourceUris": {
193 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM450e9e96877e87646fc02e814cd20b03/Media.json"
194 | }
195 | },
196 | {
197 | "sid": "SM99f72c308caf8f635887983da047c3f3",
198 | "date_created": "Thu, 26 Feb 2015 19:29:50 +0000",
199 | "date_updated": "Thu, 26 Feb 2015 19:29:51 +0000",
200 | "date_sent": "Thu, 26 Feb 2015 19:29:51 +0000",
201 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
202 | "to": "+15851427131",
203 | "from": "+11085432310",
204 | "messaging_service_sid": null,
205 | "body": "Fugiat cillum tempor irure nulla pariatur nisi adipisicing in tempor quis quis labore qui.",
206 | "status": "received",
207 | "num_segments": "1",
208 | "num_media": "0",
209 | "direction": "inbound",
210 | "api_version": "2010-04-01",
211 | "price": "-0.00750",
212 | "price_unit": "USD",
213 | "error_code": null,
214 | "error_message": null,
215 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM99f72c308caf8f635887983da047c3f3",
216 | "subresource_uris": {
217 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM99f72c308caf8f635887983da047c3f3/Media.json"
218 | },
219 | "dateCreated": "2015-02-26T19:29:50.000Z",
220 | "dateUpdated": "2015-02-26T19:29:51.000Z",
221 | "dateSent": "2015-02-26T19:29:51.000Z",
222 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
223 | "messagingServiceSid": null,
224 | "numSegments": "1",
225 | "numMedia": "0",
226 | "apiVersion": "2010-04-01",
227 | "priceUnit": "USD",
228 | "errorCode": null,
229 | "errorMessage": null,
230 | "subresourceUris": {
231 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM99f72c308caf8f635887983da047c3f3/Media.json"
232 | }
233 | },
234 | {
235 | "sid": "SM910ad3c6eae9b233d581ed4d634449fa",
236 | "date_created": "Thu, 26 Feb 2015 19:29:14 +0000",
237 | "date_updated": "Thu, 26 Feb 2015 19:29:14 +0000",
238 | "date_sent": "Thu, 26 Feb 2015 19:29:14 +0000",
239 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
240 | "to": "+11085432310",
241 | "from": "+15851427131",
242 | "messaging_service_sid": null,
243 | "body": "Anim in commodo sit anim officia magna ipsum.",
244 | "status": "delivered",
245 | "num_segments": "1",
246 | "num_media": "0",
247 | "direction": "outbound-reply",
248 | "api_version": "2010-04-01",
249 | "price": "-0.00750",
250 | "price_unit": "USD",
251 | "error_code": null,
252 | "error_message": null,
253 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM910ad3c6eae9b233d581ed4d634449fa",
254 | "subresource_uris": {
255 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM910ad3c6eae9b233d581ed4d634449fa/Media.json"
256 | },
257 | "dateCreated": "2015-02-26T19:29:14.000Z",
258 | "dateUpdated": "2015-02-26T19:29:14.000Z",
259 | "dateSent": "2015-02-26T19:29:14.000Z",
260 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
261 | "messagingServiceSid": null,
262 | "numSegments": "1",
263 | "numMedia": "0",
264 | "apiVersion": "2010-04-01",
265 | "priceUnit": "USD",
266 | "errorCode": null,
267 | "errorMessage": null,
268 | "subresourceUris": {
269 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM910ad3c6eae9b233d581ed4d634449fa/Media.json"
270 | }
271 | },
272 | {
273 | "sid": "SMddf8858e4ebb03d2deadcf7f0f93ae10",
274 | "date_created": "Thu, 26 Feb 2015 19:29:13 +0000",
275 | "date_updated": "Thu, 26 Feb 2015 19:29:14 +0000",
276 | "date_sent": "Thu, 26 Feb 2015 19:29:14 +0000",
277 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
278 | "to": "+15851427131",
279 | "from": "+11085432310",
280 | "messaging_service_sid": null,
281 | "body": "Nostrud labore eiusmod voluptate tempor non amet amet ea aute reprehenderit enim et nulla.",
282 | "status": "received",
283 | "num_segments": "1",
284 | "num_media": "0",
285 | "direction": "inbound",
286 | "api_version": "2010-04-01",
287 | "price": "-0.00750",
288 | "price_unit": "USD",
289 | "error_code": null,
290 | "error_message": null,
291 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMddf8858e4ebb03d2deadcf7f0f93ae10",
292 | "subresource_uris": {
293 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMddf8858e4ebb03d2deadcf7f0f93ae10/Media.json"
294 | },
295 | "dateCreated": "2015-02-26T19:29:13.000Z",
296 | "dateUpdated": "2015-02-26T19:29:14.000Z",
297 | "dateSent": "2015-02-26T19:29:14.000Z",
298 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
299 | "messagingServiceSid": null,
300 | "numSegments": "1",
301 | "numMedia": "0",
302 | "apiVersion": "2010-04-01",
303 | "priceUnit": "USD",
304 | "errorCode": null,
305 | "errorMessage": null,
306 | "subresourceUris": {
307 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMddf8858e4ebb03d2deadcf7f0f93ae10/Media.json"
308 | }
309 | },
310 | {
311 | "sid": "SMb72ff3fbf7906849883558b7ea2acfdd",
312 | "date_created": "Thu, 26 Feb 2015 19:28:35 +0000",
313 | "date_updated": "Thu, 26 Feb 2015 19:28:42 +0000",
314 | "date_sent": "Thu, 26 Feb 2015 19:28:41 +0000",
315 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
316 | "to": "+13088632703",
317 | "from": "+15851427131",
318 | "messaging_service_sid": null,
319 | "body": "Aliqua consectetur id commodo nisi sunt voluptate aute eu fugiat laboris reprehenderit dolor.",
320 | "status": "delivered",
321 | "num_segments": "1",
322 | "num_media": "0",
323 | "direction": "outbound-reply",
324 | "api_version": "2010-04-01",
325 | "price": "-0.00750",
326 | "price_unit": "USD",
327 | "error_code": null,
328 | "error_message": null,
329 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMb72ff3fbf7906849883558b7ea2acfdd",
330 | "subresource_uris": {
331 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMb72ff3fbf7906849883558b7ea2acfdd/Media.json"
332 | },
333 | "dateCreated": "2015-02-26T19:28:35.000Z",
334 | "dateUpdated": "2015-02-26T19:28:42.000Z",
335 | "dateSent": "2015-02-26T19:28:41.000Z",
336 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
337 | "messagingServiceSid": null,
338 | "numSegments": "1",
339 | "numMedia": "0",
340 | "apiVersion": "2010-04-01",
341 | "priceUnit": "USD",
342 | "errorCode": null,
343 | "errorMessage": null,
344 | "subresourceUris": {
345 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMb72ff3fbf7906849883558b7ea2acfdd/Media.json"
346 | }
347 | },
348 | {
349 | "sid": "SMe066c282472840f11d4881e0e317a023",
350 | "date_created": "Thu, 26 Feb 2015 19:28:35 +0000",
351 | "date_updated": "Thu, 26 Feb 2015 19:28:35 +0000",
352 | "date_sent": "Thu, 26 Feb 2015 19:28:35 +0000",
353 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
354 | "to": "+15851427131",
355 | "from": "+13088632703",
356 | "messaging_service_sid": null,
357 | "body": "Ut dolore quis fugiat id.",
358 | "status": "received",
359 | "num_segments": "1",
360 | "num_media": "0",
361 | "direction": "inbound",
362 | "api_version": "2010-04-01",
363 | "price": "-0.00750",
364 | "price_unit": "USD",
365 | "error_code": null,
366 | "error_message": null,
367 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe066c282472840f11d4881e0e317a023",
368 | "subresource_uris": {
369 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe066c282472840f11d4881e0e317a023/Media.json"
370 | },
371 | "dateCreated": "2015-02-26T19:28:35.000Z",
372 | "dateUpdated": "2015-02-26T19:28:35.000Z",
373 | "dateSent": "2015-02-26T19:28:35.000Z",
374 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
375 | "messagingServiceSid": null,
376 | "numSegments": "1",
377 | "numMedia": "0",
378 | "apiVersion": "2010-04-01",
379 | "priceUnit": "USD",
380 | "errorCode": null,
381 | "errorMessage": null,
382 | "subresourceUris": {
383 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe066c282472840f11d4881e0e317a023/Media.json"
384 | }
385 | },
386 | {
387 | "sid": "SMed30d7fa8812494516f2d54240f6a78e",
388 | "date_created": "Thu, 26 Feb 2015 19:28:34 +0000",
389 | "date_updated": "Thu, 26 Feb 2015 19:28:34 +0000",
390 | "date_sent": "Thu, 26 Feb 2015 19:28:34 +0000",
391 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
392 | "to": "+17034524023",
393 | "from": "+15851427131",
394 | "messaging_service_sid": null,
395 | "body": "Fugiat dolor aute in irure mollit occaecat elit.",
396 | "status": "delivered",
397 | "num_segments": "1",
398 | "num_media": "0",
399 | "direction": "outbound-reply",
400 | "api_version": "2010-04-01",
401 | "price": "-0.00750",
402 | "price_unit": "USD",
403 | "error_code": null,
404 | "error_message": null,
405 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMed30d7fa8812494516f2d54240f6a78e",
406 | "subresource_uris": {
407 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMed30d7fa8812494516f2d54240f6a78e/Media.json"
408 | },
409 | "dateCreated": "2015-02-26T19:28:34.000Z",
410 | "dateUpdated": "2015-02-26T19:28:34.000Z",
411 | "dateSent": "2015-02-26T19:28:34.000Z",
412 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
413 | "messagingServiceSid": null,
414 | "numSegments": "1",
415 | "numMedia": "0",
416 | "apiVersion": "2010-04-01",
417 | "priceUnit": "USD",
418 | "errorCode": null,
419 | "errorMessage": null,
420 | "subresourceUris": {
421 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMed30d7fa8812494516f2d54240f6a78e/Media.json"
422 | }
423 | },
424 | {
425 | "sid": "SMc97746bc5f4435463df7298233558106",
426 | "date_created": "Thu, 26 Feb 2015 19:28:33 +0000",
427 | "date_updated": "Thu, 26 Feb 2015 19:28:33 +0000",
428 | "date_sent": "Thu, 26 Feb 2015 19:28:33 +0000",
429 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
430 | "to": "+15851427131",
431 | "from": "+17034524023",
432 | "messaging_service_sid": null,
433 | "body": "Culpa adipisicing occaecat occaecat ipsum id commodo quis veniam.",
434 | "status": "received",
435 | "num_segments": "1",
436 | "num_media": "0",
437 | "direction": "inbound",
438 | "api_version": "2010-04-01",
439 | "price": "-0.00750",
440 | "price_unit": "USD",
441 | "error_code": null,
442 | "error_message": null,
443 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc97746bc5f4435463df7298233558106",
444 | "subresource_uris": {
445 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc97746bc5f4435463df7298233558106/Media.json"
446 | },
447 | "dateCreated": "2015-02-26T19:28:33.000Z",
448 | "dateUpdated": "2015-02-26T19:28:33.000Z",
449 | "dateSent": "2015-02-26T19:28:33.000Z",
450 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
451 | "messagingServiceSid": null,
452 | "numSegments": "1",
453 | "numMedia": "0",
454 | "apiVersion": "2010-04-01",
455 | "priceUnit": "USD",
456 | "errorCode": null,
457 | "errorMessage": null,
458 | "subresourceUris": {
459 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc97746bc5f4435463df7298233558106/Media.json"
460 | }
461 | },
462 | {
463 | "sid": "SM6c69b2aedf3211dbfa483ec53403a82c",
464 | "date_created": "Thu, 26 Feb 2015 19:28:18 +0000",
465 | "date_updated": "Thu, 26 Feb 2015 19:28:19 +0000",
466 | "date_sent": "Thu, 26 Feb 2015 19:28:19 +0000",
467 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
468 | "to": "+18714198645",
469 | "from": "+15851427131",
470 | "messaging_service_sid": null,
471 | "body": "Anim esse ipsum labore ea commodo aute do ea est velit occaecat ipsum reprehenderit.",
472 | "status": "delivered",
473 | "num_segments": "1",
474 | "num_media": "0",
475 | "direction": "outbound-reply",
476 | "api_version": "2010-04-01",
477 | "price": "-0.00750",
478 | "price_unit": "USD",
479 | "error_code": null,
480 | "error_message": null,
481 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM6c69b2aedf3211dbfa483ec53403a82c",
482 | "subresource_uris": {
483 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM6c69b2aedf3211dbfa483ec53403a82c/Media.json"
484 | },
485 | "dateCreated": "2015-02-26T19:28:18.000Z",
486 | "dateUpdated": "2015-02-26T19:28:19.000Z",
487 | "dateSent": "2015-02-26T19:28:19.000Z",
488 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
489 | "messagingServiceSid": null,
490 | "numSegments": "1",
491 | "numMedia": "0",
492 | "apiVersion": "2010-04-01",
493 | "priceUnit": "USD",
494 | "errorCode": null,
495 | "errorMessage": null,
496 | "subresourceUris": {
497 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM6c69b2aedf3211dbfa483ec53403a82c/Media.json"
498 | }
499 | },
500 | {
501 | "sid": "SMa1ad539d954f11796be6855158055618",
502 | "date_created": "Thu, 26 Feb 2015 19:28:18 +0000",
503 | "date_updated": "Thu, 26 Feb 2015 19:28:19 +0000",
504 | "date_sent": "Thu, 26 Feb 2015 19:28:19 +0000",
505 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
506 | "to": "+15851427131",
507 | "from": "+18714198645",
508 | "messaging_service_sid": null,
509 | "body": "Ut magna nostrud laborum ipsum excepteur.",
510 | "status": "received",
511 | "num_segments": "1",
512 | "num_media": "0",
513 | "direction": "inbound",
514 | "api_version": "2010-04-01",
515 | "price": "-0.00750",
516 | "price_unit": "USD",
517 | "error_code": null,
518 | "error_message": null,
519 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa1ad539d954f11796be6855158055618",
520 | "subresource_uris": {
521 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa1ad539d954f11796be6855158055618/Media.json"
522 | },
523 | "dateCreated": "2015-02-26T19:28:18.000Z",
524 | "dateUpdated": "2015-02-26T19:28:19.000Z",
525 | "dateSent": "2015-02-26T19:28:19.000Z",
526 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
527 | "messagingServiceSid": null,
528 | "numSegments": "1",
529 | "numMedia": "0",
530 | "apiVersion": "2010-04-01",
531 | "priceUnit": "USD",
532 | "errorCode": null,
533 | "errorMessage": null,
534 | "subresourceUris": {
535 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa1ad539d954f11796be6855158055618/Media.json"
536 | }
537 | },
538 | {
539 | "sid": "SM903010630adb89d271aa170ae634f210",
540 | "date_created": "Thu, 26 Feb 2015 19:27:41 +0000",
541 | "date_updated": "Thu, 26 Feb 2015 19:27:43 +0000",
542 | "date_sent": "Thu, 26 Feb 2015 19:27:41 +0000",
543 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
544 | "to": "+11085432310",
545 | "from": "+15851427131",
546 | "messaging_service_sid": null,
547 | "body": "Fugiat mollit consectetur qui aute reprehenderit labore eu.",
548 | "status": "delivered",
549 | "num_segments": "1",
550 | "num_media": "0",
551 | "direction": "outbound-reply",
552 | "api_version": "2010-04-01",
553 | "price": "-0.00750",
554 | "price_unit": "USD",
555 | "error_code": null,
556 | "error_message": null,
557 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM903010630adb89d271aa170ae634f210",
558 | "subresource_uris": {
559 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM903010630adb89d271aa170ae634f210/Media.json"
560 | },
561 | "dateCreated": "2015-02-26T19:27:41.000Z",
562 | "dateUpdated": "2015-02-26T19:27:43.000Z",
563 | "dateSent": "2015-02-26T19:27:41.000Z",
564 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
565 | "messagingServiceSid": null,
566 | "numSegments": "1",
567 | "numMedia": "0",
568 | "apiVersion": "2010-04-01",
569 | "priceUnit": "USD",
570 | "errorCode": null,
571 | "errorMessage": null,
572 | "subresourceUris": {
573 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM903010630adb89d271aa170ae634f210/Media.json"
574 | }
575 | },
576 | {
577 | "sid": "SMf3d0fdcc7852a3d47f2d6361064ea5cb",
578 | "date_created": "Thu, 26 Feb 2015 19:27:40 +0000",
579 | "date_updated": "Thu, 26 Feb 2015 19:27:41 +0000",
580 | "date_sent": "Thu, 26 Feb 2015 19:27:41 +0000",
581 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
582 | "to": "+15851427131",
583 | "from": "+11085432310",
584 | "messaging_service_sid": null,
585 | "body": "Non id commodo ullamco consectetur deserunt.",
586 | "status": "received",
587 | "num_segments": "1",
588 | "num_media": "0",
589 | "direction": "inbound",
590 | "api_version": "2010-04-01",
591 | "price": "-0.00750",
592 | "price_unit": "USD",
593 | "error_code": null,
594 | "error_message": null,
595 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMf3d0fdcc7852a3d47f2d6361064ea5cb",
596 | "subresource_uris": {
597 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMf3d0fdcc7852a3d47f2d6361064ea5cb/Media.json"
598 | },
599 | "dateCreated": "2015-02-26T19:27:40.000Z",
600 | "dateUpdated": "2015-02-26T19:27:41.000Z",
601 | "dateSent": "2015-02-26T19:27:41.000Z",
602 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
603 | "messagingServiceSid": null,
604 | "numSegments": "1",
605 | "numMedia": "0",
606 | "apiVersion": "2010-04-01",
607 | "priceUnit": "USD",
608 | "errorCode": null,
609 | "errorMessage": null,
610 | "subresourceUris": {
611 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMf3d0fdcc7852a3d47f2d6361064ea5cb/Media.json"
612 | }
613 | },
614 | {
615 | "sid": "SMc6114c85b88fe9d5fce514a3d6fd0fe5",
616 | "date_created": "Thu, 26 Feb 2015 19:27:24 +0000",
617 | "date_updated": "Thu, 26 Feb 2015 19:27:24 +0000",
618 | "date_sent": "Thu, 26 Feb 2015 19:27:24 +0000",
619 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
620 | "to": "+13088632703",
621 | "from": "+15851427131",
622 | "messaging_service_sid": null,
623 | "body": "Aute commodo Lorem magna consequat reprehenderit id elit exercitation et reprehenderit.",
624 | "status": "delivered",
625 | "num_segments": "1",
626 | "num_media": "0",
627 | "direction": "outbound-reply",
628 | "api_version": "2010-04-01",
629 | "price": "-0.00750",
630 | "price_unit": "USD",
631 | "error_code": null,
632 | "error_message": null,
633 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc6114c85b88fe9d5fce514a3d6fd0fe5",
634 | "subresource_uris": {
635 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc6114c85b88fe9d5fce514a3d6fd0fe5/Media.json"
636 | },
637 | "dateCreated": "2015-02-26T19:27:24.000Z",
638 | "dateUpdated": "2015-02-26T19:27:24.000Z",
639 | "dateSent": "2015-02-26T19:27:24.000Z",
640 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
641 | "messagingServiceSid": null,
642 | "numSegments": "1",
643 | "numMedia": "0",
644 | "apiVersion": "2010-04-01",
645 | "priceUnit": "USD",
646 | "errorCode": null,
647 | "errorMessage": null,
648 | "subresourceUris": {
649 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc6114c85b88fe9d5fce514a3d6fd0fe5/Media.json"
650 | }
651 | },
652 | {
653 | "sid": "SM3366851272a474fab8d8cf00ea4250e3",
654 | "date_created": "Thu, 26 Feb 2015 19:27:24 +0000",
655 | "date_updated": "Thu, 26 Feb 2015 19:27:24 +0000",
656 | "date_sent": "Thu, 26 Feb 2015 19:27:24 +0000",
657 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
658 | "to": "+15851427131",
659 | "from": "+13088632703",
660 | "messaging_service_sid": null,
661 | "body": "Amet est incididunt aute fugiat id adipisicing.",
662 | "status": "received",
663 | "num_segments": "1",
664 | "num_media": "0",
665 | "direction": "inbound",
666 | "api_version": "2010-04-01",
667 | "price": "-0.00750",
668 | "price_unit": "USD",
669 | "error_code": null,
670 | "error_message": null,
671 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM3366851272a474fab8d8cf00ea4250e3",
672 | "subresource_uris": {
673 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM3366851272a474fab8d8cf00ea4250e3/Media.json"
674 | },
675 | "dateCreated": "2015-02-26T19:27:24.000Z",
676 | "dateUpdated": "2015-02-26T19:27:24.000Z",
677 | "dateSent": "2015-02-26T19:27:24.000Z",
678 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
679 | "messagingServiceSid": null,
680 | "numSegments": "1",
681 | "numMedia": "0",
682 | "apiVersion": "2010-04-01",
683 | "priceUnit": "USD",
684 | "errorCode": null,
685 | "errorMessage": null,
686 | "subresourceUris": {
687 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM3366851272a474fab8d8cf00ea4250e3/Media.json"
688 | }
689 | },
690 | {
691 | "sid": "SM1cb9cc90ba3a3cc5c01b2d9bc20f9720",
692 | "date_created": "Thu, 26 Feb 2015 19:18:34 +0000",
693 | "date_updated": "Thu, 26 Feb 2015 19:18:34 +0000",
694 | "date_sent": "Thu, 26 Feb 2015 19:18:34 +0000",
695 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
696 | "to": "+11582979687",
697 | "from": "+15851427131",
698 | "messaging_service_sid": null,
699 | "body": "Proident qui magna cupidatat eiusmod veniam laborum Lorem incididunt sint.",
700 | "status": "delivered",
701 | "num_segments": "1",
702 | "num_media": "0",
703 | "direction": "outbound-reply",
704 | "api_version": "2010-04-01",
705 | "price": "-0.00750",
706 | "price_unit": "USD",
707 | "error_code": null,
708 | "error_message": null,
709 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM1cb9cc90ba3a3cc5c01b2d9bc20f9720",
710 | "subresource_uris": {
711 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM1cb9cc90ba3a3cc5c01b2d9bc20f9720/Media.json"
712 | },
713 | "dateCreated": "2015-02-26T19:18:34.000Z",
714 | "dateUpdated": "2015-02-26T19:18:34.000Z",
715 | "dateSent": "2015-02-26T19:18:34.000Z",
716 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
717 | "messagingServiceSid": null,
718 | "numSegments": "1",
719 | "numMedia": "0",
720 | "apiVersion": "2010-04-01",
721 | "priceUnit": "USD",
722 | "errorCode": null,
723 | "errorMessage": null,
724 | "subresourceUris": {
725 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM1cb9cc90ba3a3cc5c01b2d9bc20f9720/Media.json"
726 | }
727 | },
728 | {
729 | "sid": "SM419db52c4b77f4648c80ad9bd3fa52f3",
730 | "date_created": "Thu, 26 Feb 2015 19:18:33 +0000",
731 | "date_updated": "Thu, 26 Feb 2015 19:18:34 +0000",
732 | "date_sent": "Thu, 26 Feb 2015 19:18:34 +0000",
733 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
734 | "to": "+15851427131",
735 | "from": "+11582979687",
736 | "messaging_service_sid": null,
737 | "body": "Excepteur ut magna ea laboris deserunt ex.",
738 | "status": "received",
739 | "num_segments": "1",
740 | "num_media": "0",
741 | "direction": "inbound",
742 | "api_version": "2010-04-01",
743 | "price": "-0.00750",
744 | "price_unit": "USD",
745 | "error_code": null,
746 | "error_message": null,
747 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM419db52c4b77f4648c80ad9bd3fa52f3",
748 | "subresource_uris": {
749 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM419db52c4b77f4648c80ad9bd3fa52f3/Media.json"
750 | },
751 | "dateCreated": "2015-02-26T19:18:33.000Z",
752 | "dateUpdated": "2015-02-26T19:18:34.000Z",
753 | "dateSent": "2015-02-26T19:18:34.000Z",
754 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
755 | "messagingServiceSid": null,
756 | "numSegments": "1",
757 | "numMedia": "0",
758 | "apiVersion": "2010-04-01",
759 | "priceUnit": "USD",
760 | "errorCode": null,
761 | "errorMessage": null,
762 | "subresourceUris": {
763 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM419db52c4b77f4648c80ad9bd3fa52f3/Media.json"
764 | }
765 | },
766 | {
767 | "sid": "SM80fcd6c1daf3f7aa48e9d1e2671d4745",
768 | "date_created": "Thu, 26 Feb 2015 19:17:56 +0000",
769 | "date_updated": "Thu, 26 Feb 2015 19:17:56 +0000",
770 | "date_sent": "Thu, 26 Feb 2015 19:17:56 +0000",
771 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
772 | "to": "+11582979687",
773 | "from": "+15851427131",
774 | "messaging_service_sid": null,
775 | "body": "Elit Lorem pariatur dolore sint non culpa occaecat ipsum do sunt irure cillum eu ad.",
776 | "status": "delivered",
777 | "num_segments": "1",
778 | "num_media": "0",
779 | "direction": "outbound-reply",
780 | "api_version": "2010-04-01",
781 | "price": "-0.00750",
782 | "price_unit": "USD",
783 | "error_code": null,
784 | "error_message": null,
785 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM80fcd6c1daf3f7aa48e9d1e2671d4745",
786 | "subresource_uris": {
787 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM80fcd6c1daf3f7aa48e9d1e2671d4745/Media.json"
788 | },
789 | "dateCreated": "2015-02-26T19:17:56.000Z",
790 | "dateUpdated": "2015-02-26T19:17:56.000Z",
791 | "dateSent": "2015-02-26T19:17:56.000Z",
792 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
793 | "messagingServiceSid": null,
794 | "numSegments": "1",
795 | "numMedia": "0",
796 | "apiVersion": "2010-04-01",
797 | "priceUnit": "USD",
798 | "errorCode": null,
799 | "errorMessage": null,
800 | "subresourceUris": {
801 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM80fcd6c1daf3f7aa48e9d1e2671d4745/Media.json"
802 | }
803 | },
804 | {
805 | "sid": "SMe6a7a63327472326de1d49f490be7774",
806 | "date_created": "Thu, 26 Feb 2015 19:17:55 +0000",
807 | "date_updated": "Thu, 26 Feb 2015 19:17:56 +0000",
808 | "date_sent": "Thu, 26 Feb 2015 19:17:56 +0000",
809 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
810 | "to": "+15851427131",
811 | "from": "+11582979687",
812 | "messaging_service_sid": null,
813 | "body": "Laboris irure magna ut nostrud.",
814 | "status": "received",
815 | "num_segments": "1",
816 | "num_media": "0",
817 | "direction": "inbound",
818 | "api_version": "2010-04-01",
819 | "price": "-0.00750",
820 | "price_unit": "USD",
821 | "error_code": null,
822 | "error_message": null,
823 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe6a7a63327472326de1d49f490be7774",
824 | "subresource_uris": {
825 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe6a7a63327472326de1d49f490be7774/Media.json"
826 | },
827 | "dateCreated": "2015-02-26T19:17:55.000Z",
828 | "dateUpdated": "2015-02-26T19:17:56.000Z",
829 | "dateSent": "2015-02-26T19:17:56.000Z",
830 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
831 | "messagingServiceSid": null,
832 | "numSegments": "1",
833 | "numMedia": "0",
834 | "apiVersion": "2010-04-01",
835 | "priceUnit": "USD",
836 | "errorCode": null,
837 | "errorMessage": null,
838 | "subresourceUris": {
839 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe6a7a63327472326de1d49f490be7774/Media.json"
840 | }
841 | },
842 | {
843 | "sid": "SM87312304f5f896e147e93300be2466b2",
844 | "date_created": "Thu, 26 Feb 2015 19:17:46 +0000",
845 | "date_updated": "Thu, 26 Feb 2015 19:17:47 +0000",
846 | "date_sent": "Thu, 26 Feb 2015 19:17:46 +0000",
847 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
848 | "to": "+13088632703",
849 | "from": "+15851427131",
850 | "messaging_service_sid": null,
851 | "body": "Quis exercitation dolore occaecat aute qui fugiat labore excepteur consequat est officia.",
852 | "status": "delivered",
853 | "num_segments": "1",
854 | "num_media": "0",
855 | "direction": "outbound-reply",
856 | "api_version": "2010-04-01",
857 | "price": "-0.00750",
858 | "price_unit": "USD",
859 | "error_code": null,
860 | "error_message": null,
861 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM87312304f5f896e147e93300be2466b2",
862 | "subresource_uris": {
863 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM87312304f5f896e147e93300be2466b2/Media.json"
864 | },
865 | "dateCreated": "2015-02-26T19:17:46.000Z",
866 | "dateUpdated": "2015-02-26T19:17:47.000Z",
867 | "dateSent": "2015-02-26T19:17:46.000Z",
868 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
869 | "messagingServiceSid": null,
870 | "numSegments": "1",
871 | "numMedia": "0",
872 | "apiVersion": "2010-04-01",
873 | "priceUnit": "USD",
874 | "errorCode": null,
875 | "errorMessage": null,
876 | "subresourceUris": {
877 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM87312304f5f896e147e93300be2466b2/Media.json"
878 | }
879 | },
880 | {
881 | "sid": "SM2a6b78fb2f4c1cf301986e335c0ff035",
882 | "date_created": "Thu, 26 Feb 2015 19:17:46 +0000",
883 | "date_updated": "Thu, 26 Feb 2015 19:17:46 +0000",
884 | "date_sent": "Thu, 26 Feb 2015 19:17:46 +0000",
885 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
886 | "to": "+15851427131",
887 | "from": "+13088632703",
888 | "messaging_service_sid": null,
889 | "body": "Et eiusmod laboris esse labore anim commodo nostrud.",
890 | "status": "received",
891 | "num_segments": "1",
892 | "num_media": "0",
893 | "direction": "inbound",
894 | "api_version": "2010-04-01",
895 | "price": "-0.00750",
896 | "price_unit": "USD",
897 | "error_code": null,
898 | "error_message": null,
899 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2a6b78fb2f4c1cf301986e335c0ff035",
900 | "subresource_uris": {
901 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2a6b78fb2f4c1cf301986e335c0ff035/Media.json"
902 | },
903 | "dateCreated": "2015-02-26T19:17:46.000Z",
904 | "dateUpdated": "2015-02-26T19:17:46.000Z",
905 | "dateSent": "2015-02-26T19:17:46.000Z",
906 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
907 | "messagingServiceSid": null,
908 | "numSegments": "1",
909 | "numMedia": "0",
910 | "apiVersion": "2010-04-01",
911 | "priceUnit": "USD",
912 | "errorCode": null,
913 | "errorMessage": null,
914 | "subresourceUris": {
915 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2a6b78fb2f4c1cf301986e335c0ff035/Media.json"
916 | }
917 | },
918 | {
919 | "sid": "SM8ce08e0c2ee6b2cbd354327dcc17234e",
920 | "date_created": "Thu, 26 Feb 2015 19:17:32 +0000",
921 | "date_updated": "Thu, 26 Feb 2015 19:17:33 +0000",
922 | "date_sent": "Thu, 26 Feb 2015 19:17:32 +0000",
923 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
924 | "to": "+11582979687",
925 | "from": "+15851427131",
926 | "messaging_service_sid": null,
927 | "body": "Lorem deserunt elit veniam Lorem fugiat nulla cupidatat sint pariatur elit elit.",
928 | "status": "delivered",
929 | "num_segments": "1",
930 | "num_media": "0",
931 | "direction": "outbound-reply",
932 | "api_version": "2010-04-01",
933 | "price": "-0.00750",
934 | "price_unit": "USD",
935 | "error_code": null,
936 | "error_message": null,
937 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM8ce08e0c2ee6b2cbd354327dcc17234e",
938 | "subresource_uris": {
939 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM8ce08e0c2ee6b2cbd354327dcc17234e/Media.json"
940 | },
941 | "dateCreated": "2015-02-26T19:17:32.000Z",
942 | "dateUpdated": "2015-02-26T19:17:33.000Z",
943 | "dateSent": "2015-02-26T19:17:32.000Z",
944 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
945 | "messagingServiceSid": null,
946 | "numSegments": "1",
947 | "numMedia": "0",
948 | "apiVersion": "2010-04-01",
949 | "priceUnit": "USD",
950 | "errorCode": null,
951 | "errorMessage": null,
952 | "subresourceUris": {
953 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM8ce08e0c2ee6b2cbd354327dcc17234e/Media.json"
954 | }
955 | },
956 | {
957 | "sid": "SMc46c42888de4fec754d510b184037248",
958 | "date_created": "Thu, 26 Feb 2015 19:17:28 +0000",
959 | "date_updated": "Thu, 26 Feb 2015 19:17:32 +0000",
960 | "date_sent": "Thu, 26 Feb 2015 19:17:32 +0000",
961 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
962 | "to": "+15851427131",
963 | "from": "+11582979687",
964 | "messaging_service_sid": null,
965 | "body": "Dolore est sint tempor incididunt nisi anim sit.",
966 | "status": "received",
967 | "num_segments": "1",
968 | "num_media": "0",
969 | "direction": "inbound",
970 | "api_version": "2010-04-01",
971 | "price": "-0.00750",
972 | "price_unit": "USD",
973 | "error_code": null,
974 | "error_message": null,
975 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc46c42888de4fec754d510b184037248",
976 | "subresource_uris": {
977 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc46c42888de4fec754d510b184037248/Media.json"
978 | },
979 | "dateCreated": "2015-02-26T19:17:28.000Z",
980 | "dateUpdated": "2015-02-26T19:17:32.000Z",
981 | "dateSent": "2015-02-26T19:17:32.000Z",
982 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
983 | "messagingServiceSid": null,
984 | "numSegments": "1",
985 | "numMedia": "0",
986 | "apiVersion": "2010-04-01",
987 | "priceUnit": "USD",
988 | "errorCode": null,
989 | "errorMessage": null,
990 | "subresourceUris": {
991 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMc46c42888de4fec754d510b184037248/Media.json"
992 | }
993 | },
994 | {
995 | "sid": "SM61f5b518dd5d69c58a5cc6ebdead4554",
996 | "date_created": "Thu, 26 Feb 2015 19:17:16 +0000",
997 | "date_updated": "Thu, 26 Feb 2015 19:17:17 +0000",
998 | "date_sent": "Thu, 26 Feb 2015 19:17:17 +0000",
999 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1000 | "to": "+11582979687",
1001 | "from": "+15851427131",
1002 | "messaging_service_sid": null,
1003 | "body": "Non adipisicing deserunt Lorem deserunt ex cillum qui ipsum ullamco incididunt est laboris cupidatat excepteur.",
1004 | "status": "delivered",
1005 | "num_segments": "1",
1006 | "num_media": "0",
1007 | "direction": "outbound-reply",
1008 | "api_version": "2010-04-01",
1009 | "price": "-0.00750",
1010 | "price_unit": "USD",
1011 | "error_code": null,
1012 | "error_message": null,
1013 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM61f5b518dd5d69c58a5cc6ebdead4554",
1014 | "subresource_uris": {
1015 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM61f5b518dd5d69c58a5cc6ebdead4554/Media.json"
1016 | },
1017 | "dateCreated": "2015-02-26T19:17:16.000Z",
1018 | "dateUpdated": "2015-02-26T19:17:17.000Z",
1019 | "dateSent": "2015-02-26T19:17:17.000Z",
1020 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1021 | "messagingServiceSid": null,
1022 | "numSegments": "1",
1023 | "numMedia": "0",
1024 | "apiVersion": "2010-04-01",
1025 | "priceUnit": "USD",
1026 | "errorCode": null,
1027 | "errorMessage": null,
1028 | "subresourceUris": {
1029 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM61f5b518dd5d69c58a5cc6ebdead4554/Media.json"
1030 | }
1031 | },
1032 | {
1033 | "sid": "SM7a17e1dcaec3519fad5ae166b8cc3393",
1034 | "date_created": "Thu, 26 Feb 2015 19:17:09 +0000",
1035 | "date_updated": "Thu, 26 Feb 2015 19:17:17 +0000",
1036 | "date_sent": "Thu, 26 Feb 2015 19:17:17 +0000",
1037 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1038 | "to": "+15851427131",
1039 | "from": "+11582979687",
1040 | "messaging_service_sid": null,
1041 | "body": "Tempor voluptate sint commodo deserunt laboris sint aute dolore officia.",
1042 | "status": "received",
1043 | "num_segments": "1",
1044 | "num_media": "0",
1045 | "direction": "inbound",
1046 | "api_version": "2010-04-01",
1047 | "price": "-0.00750",
1048 | "price_unit": "USD",
1049 | "error_code": null,
1050 | "error_message": null,
1051 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7a17e1dcaec3519fad5ae166b8cc3393",
1052 | "subresource_uris": {
1053 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7a17e1dcaec3519fad5ae166b8cc3393/Media.json"
1054 | },
1055 | "dateCreated": "2015-02-26T19:17:09.000Z",
1056 | "dateUpdated": "2015-02-26T19:17:17.000Z",
1057 | "dateSent": "2015-02-26T19:17:17.000Z",
1058 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1059 | "messagingServiceSid": null,
1060 | "numSegments": "1",
1061 | "numMedia": "0",
1062 | "apiVersion": "2010-04-01",
1063 | "priceUnit": "USD",
1064 | "errorCode": null,
1065 | "errorMessage": null,
1066 | "subresourceUris": {
1067 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7a17e1dcaec3519fad5ae166b8cc3393/Media.json"
1068 | }
1069 | },
1070 | {
1071 | "sid": "SM7c31f6898deec1e444cfdca334f816e7",
1072 | "date_created": "Thu, 26 Feb 2015 19:16:01 +0000",
1073 | "date_updated": "Thu, 26 Feb 2015 19:16:01 +0000",
1074 | "date_sent": "Thu, 26 Feb 2015 19:16:01 +0000",
1075 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1076 | "to": "+11582979687",
1077 | "from": "+15851427131",
1078 | "messaging_service_sid": null,
1079 | "body": "Eiusmod ea mollit occaecat magna.",
1080 | "status": "delivered",
1081 | "num_segments": "1",
1082 | "num_media": "0",
1083 | "direction": "outbound-reply",
1084 | "api_version": "2010-04-01",
1085 | "price": "-0.00750",
1086 | "price_unit": "USD",
1087 | "error_code": null,
1088 | "error_message": null,
1089 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7c31f6898deec1e444cfdca334f816e7",
1090 | "subresource_uris": {
1091 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7c31f6898deec1e444cfdca334f816e7/Media.json"
1092 | },
1093 | "dateCreated": "2015-02-26T19:16:01.000Z",
1094 | "dateUpdated": "2015-02-26T19:16:01.000Z",
1095 | "dateSent": "2015-02-26T19:16:01.000Z",
1096 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1097 | "messagingServiceSid": null,
1098 | "numSegments": "1",
1099 | "numMedia": "0",
1100 | "apiVersion": "2010-04-01",
1101 | "priceUnit": "USD",
1102 | "errorCode": null,
1103 | "errorMessage": null,
1104 | "subresourceUris": {
1105 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7c31f6898deec1e444cfdca334f816e7/Media.json"
1106 | }
1107 | },
1108 | {
1109 | "sid": "SMe91e716bd9ac85ca825884236707d94a",
1110 | "date_created": "Thu, 26 Feb 2015 19:16:00 +0000",
1111 | "date_updated": "Thu, 26 Feb 2015 19:16:01 +0000",
1112 | "date_sent": "Thu, 26 Feb 2015 19:16:01 +0000",
1113 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1114 | "to": "+15851427131",
1115 | "from": "+11582979687",
1116 | "messaging_service_sid": null,
1117 | "body": "Laboris enim exercitation ex qui.",
1118 | "status": "received",
1119 | "num_segments": "1",
1120 | "num_media": "0",
1121 | "direction": "inbound",
1122 | "api_version": "2010-04-01",
1123 | "price": "-0.00750",
1124 | "price_unit": "USD",
1125 | "error_code": null,
1126 | "error_message": null,
1127 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe91e716bd9ac85ca825884236707d94a",
1128 | "subresource_uris": {
1129 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe91e716bd9ac85ca825884236707d94a/Media.json"
1130 | },
1131 | "dateCreated": "2015-02-26T19:16:00.000Z",
1132 | "dateUpdated": "2015-02-26T19:16:01.000Z",
1133 | "dateSent": "2015-02-26T19:16:01.000Z",
1134 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1135 | "messagingServiceSid": null,
1136 | "numSegments": "1",
1137 | "numMedia": "0",
1138 | "apiVersion": "2010-04-01",
1139 | "priceUnit": "USD",
1140 | "errorCode": null,
1141 | "errorMessage": null,
1142 | "subresourceUris": {
1143 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMe91e716bd9ac85ca825884236707d94a/Media.json"
1144 | }
1145 | },
1146 | {
1147 | "sid": "SMf8ded2e6b2282623878191196eb763a2",
1148 | "date_created": "Thu, 26 Feb 2015 19:15:34 +0000",
1149 | "date_updated": "Thu, 26 Feb 2015 19:15:34 +0000",
1150 | "date_sent": "Thu, 26 Feb 2015 19:15:34 +0000",
1151 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1152 | "to": "+11085432310",
1153 | "from": "+15851427131",
1154 | "messaging_service_sid": null,
1155 | "body": "Irure cupidatat et consequat elit.",
1156 | "status": "delivered",
1157 | "num_segments": "1",
1158 | "num_media": "0",
1159 | "direction": "outbound-reply",
1160 | "api_version": "2010-04-01",
1161 | "price": "-0.00750",
1162 | "price_unit": "USD",
1163 | "error_code": null,
1164 | "error_message": null,
1165 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMf8ded2e6b2282623878191196eb763a2",
1166 | "subresource_uris": {
1167 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMf8ded2e6b2282623878191196eb763a2/Media.json"
1168 | },
1169 | "dateCreated": "2015-02-26T19:15:34.000Z",
1170 | "dateUpdated": "2015-02-26T19:15:34.000Z",
1171 | "dateSent": "2015-02-26T19:15:34.000Z",
1172 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1173 | "messagingServiceSid": null,
1174 | "numSegments": "1",
1175 | "numMedia": "0",
1176 | "apiVersion": "2010-04-01",
1177 | "priceUnit": "USD",
1178 | "errorCode": null,
1179 | "errorMessage": null,
1180 | "subresourceUris": {
1181 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMf8ded2e6b2282623878191196eb763a2/Media.json"
1182 | }
1183 | },
1184 | {
1185 | "sid": "SMfae68bb540e8fa21ebf0f3d05b76ae23",
1186 | "date_created": "Thu, 26 Feb 2015 19:15:33 +0000",
1187 | "date_updated": "Thu, 26 Feb 2015 19:15:34 +0000",
1188 | "date_sent": "Thu, 26 Feb 2015 19:15:34 +0000",
1189 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1190 | "to": "+15851427131",
1191 | "from": "+11085432310",
1192 | "messaging_service_sid": null,
1193 | "body": "Sunt ea est aliquip pariatur incididunt labore ut.",
1194 | "status": "received",
1195 | "num_segments": "1",
1196 | "num_media": "0",
1197 | "direction": "inbound",
1198 | "api_version": "2010-04-01",
1199 | "price": "-0.00750",
1200 | "price_unit": "USD",
1201 | "error_code": null,
1202 | "error_message": null,
1203 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMfae68bb540e8fa21ebf0f3d05b76ae23",
1204 | "subresource_uris": {
1205 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMfae68bb540e8fa21ebf0f3d05b76ae23/Media.json"
1206 | },
1207 | "dateCreated": "2015-02-26T19:15:33.000Z",
1208 | "dateUpdated": "2015-02-26T19:15:34.000Z",
1209 | "dateSent": "2015-02-26T19:15:34.000Z",
1210 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1211 | "messagingServiceSid": null,
1212 | "numSegments": "1",
1213 | "numMedia": "0",
1214 | "apiVersion": "2010-04-01",
1215 | "priceUnit": "USD",
1216 | "errorCode": null,
1217 | "errorMessage": null,
1218 | "subresourceUris": {
1219 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMfae68bb540e8fa21ebf0f3d05b76ae23/Media.json"
1220 | }
1221 | },
1222 | {
1223 | "sid": "SMce1e84e8dfdfc5ce8a6f23eaf02d4723",
1224 | "date_created": "Thu, 26 Feb 2015 19:13:19 +0000",
1225 | "date_updated": "Thu, 26 Feb 2015 19:13:19 +0000",
1226 | "date_sent": "Thu, 26 Feb 2015 19:13:19 +0000",
1227 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1228 | "to": "+13088632703",
1229 | "from": "+15851427131",
1230 | "messaging_service_sid": null,
1231 | "body": "Veniam quis eiusmod pariatur fugiat ad elit ullamco irure consectetur.",
1232 | "status": "delivered",
1233 | "num_segments": "1",
1234 | "num_media": "0",
1235 | "direction": "outbound-reply",
1236 | "api_version": "2010-04-01",
1237 | "price": "-0.00750",
1238 | "price_unit": "USD",
1239 | "error_code": null,
1240 | "error_message": null,
1241 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMce1e84e8dfdfc5ce8a6f23eaf02d4723",
1242 | "subresource_uris": {
1243 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMce1e84e8dfdfc5ce8a6f23eaf02d4723/Media.json"
1244 | },
1245 | "dateCreated": "2015-02-26T19:13:19.000Z",
1246 | "dateUpdated": "2015-02-26T19:13:19.000Z",
1247 | "dateSent": "2015-02-26T19:13:19.000Z",
1248 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1249 | "messagingServiceSid": null,
1250 | "numSegments": "1",
1251 | "numMedia": "0",
1252 | "apiVersion": "2010-04-01",
1253 | "priceUnit": "USD",
1254 | "errorCode": null,
1255 | "errorMessage": null,
1256 | "subresourceUris": {
1257 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMce1e84e8dfdfc5ce8a6f23eaf02d4723/Media.json"
1258 | }
1259 | },
1260 | {
1261 | "sid": "SM2ddf566d4891bfce2920c67be3ea0c1e",
1262 | "date_created": "Thu, 26 Feb 2015 19:13:18 +0000",
1263 | "date_updated": "Thu, 26 Feb 2015 19:13:19 +0000",
1264 | "date_sent": "Thu, 26 Feb 2015 19:13:19 +0000",
1265 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1266 | "to": "+15851427131",
1267 | "from": "+13088632703",
1268 | "messaging_service_sid": null,
1269 | "body": "Cupidatat pariatur dolore esse proident amet id aliqua eiusmod minim adipisicing Lorem nulla Lorem Lorem.",
1270 | "status": "received",
1271 | "num_segments": "1",
1272 | "num_media": "0",
1273 | "direction": "inbound",
1274 | "api_version": "2010-04-01",
1275 | "price": "-0.00750",
1276 | "price_unit": "USD",
1277 | "error_code": null,
1278 | "error_message": null,
1279 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2ddf566d4891bfce2920c67be3ea0c1e",
1280 | "subresource_uris": {
1281 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2ddf566d4891bfce2920c67be3ea0c1e/Media.json"
1282 | },
1283 | "dateCreated": "2015-02-26T19:13:18.000Z",
1284 | "dateUpdated": "2015-02-26T19:13:19.000Z",
1285 | "dateSent": "2015-02-26T19:13:19.000Z",
1286 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1287 | "messagingServiceSid": null,
1288 | "numSegments": "1",
1289 | "numMedia": "0",
1290 | "apiVersion": "2010-04-01",
1291 | "priceUnit": "USD",
1292 | "errorCode": null,
1293 | "errorMessage": null,
1294 | "subresourceUris": {
1295 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2ddf566d4891bfce2920c67be3ea0c1e/Media.json"
1296 | }
1297 | },
1298 | {
1299 | "sid": "SM28be53db114f58075510c6ebd8722a4f",
1300 | "date_created": "Mon, 09 Feb 2015 23:10:34 +0000",
1301 | "date_updated": "Mon, 09 Feb 2015 23:10:34 +0000",
1302 | "date_sent": "Mon, 09 Feb 2015 23:10:34 +0000",
1303 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1304 | "to": "+12597150948",
1305 | "from": "+15851427131",
1306 | "messaging_service_sid": null,
1307 | "body": "Cupidatat ut velit laboris laborum deserunt incididunt cupidatat.",
1308 | "status": "delivered",
1309 | "num_segments": "1",
1310 | "num_media": "0",
1311 | "direction": "outbound-reply",
1312 | "api_version": "2010-04-01",
1313 | "price": "-0.00750",
1314 | "price_unit": "USD",
1315 | "error_code": null,
1316 | "error_message": null,
1317 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM28be53db114f58075510c6ebd8722a4f",
1318 | "subresource_uris": {
1319 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM28be53db114f58075510c6ebd8722a4f/Media.json"
1320 | },
1321 | "dateCreated": "2015-02-09T23:10:34.000Z",
1322 | "dateUpdated": "2015-02-09T23:10:34.000Z",
1323 | "dateSent": "2015-02-09T23:10:34.000Z",
1324 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1325 | "messagingServiceSid": null,
1326 | "numSegments": "1",
1327 | "numMedia": "0",
1328 | "apiVersion": "2010-04-01",
1329 | "priceUnit": "USD",
1330 | "errorCode": null,
1331 | "errorMessage": null,
1332 | "subresourceUris": {
1333 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM28be53db114f58075510c6ebd8722a4f/Media.json"
1334 | }
1335 | },
1336 | {
1337 | "sid": "SM8d8f44cf6205a27a87c9aab0d57453d7",
1338 | "date_created": "Mon, 09 Feb 2015 23:10:33 +0000",
1339 | "date_updated": "Mon, 09 Feb 2015 23:10:34 +0000",
1340 | "date_sent": "Mon, 09 Feb 2015 23:10:34 +0000",
1341 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1342 | "to": "+15851427131",
1343 | "from": "+12597150948",
1344 | "messaging_service_sid": null,
1345 | "body": "Exercitation est officia tempor magna amet laborum ad aliqua ipsum.",
1346 | "status": "received",
1347 | "num_segments": "1",
1348 | "num_media": "0",
1349 | "direction": "inbound",
1350 | "api_version": "2010-04-01",
1351 | "price": "-0.00750",
1352 | "price_unit": "USD",
1353 | "error_code": null,
1354 | "error_message": null,
1355 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM8d8f44cf6205a27a87c9aab0d57453d7",
1356 | "subresource_uris": {
1357 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM8d8f44cf6205a27a87c9aab0d57453d7/Media.json"
1358 | },
1359 | "dateCreated": "2015-02-09T23:10:33.000Z",
1360 | "dateUpdated": "2015-02-09T23:10:34.000Z",
1361 | "dateSent": "2015-02-09T23:10:34.000Z",
1362 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1363 | "messagingServiceSid": null,
1364 | "numSegments": "1",
1365 | "numMedia": "0",
1366 | "apiVersion": "2010-04-01",
1367 | "priceUnit": "USD",
1368 | "errorCode": null,
1369 | "errorMessage": null,
1370 | "subresourceUris": {
1371 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM8d8f44cf6205a27a87c9aab0d57453d7/Media.json"
1372 | }
1373 | },
1374 | {
1375 | "sid": "SMa4008eec5726061f3d58eb1f989f2497",
1376 | "date_created": "Mon, 09 Feb 2015 23:09:46 +0000",
1377 | "date_updated": "Mon, 09 Feb 2015 23:09:47 +0000",
1378 | "date_sent": "Mon, 09 Feb 2015 23:09:46 +0000",
1379 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1380 | "to": "+12597150948",
1381 | "from": "+15851427131",
1382 | "messaging_service_sid": null,
1383 | "body": "Velit reprehenderit magna sit nostrud eu nulla nostrud officia est sint nostrud eu.",
1384 | "status": "delivered",
1385 | "num_segments": "1",
1386 | "num_media": "0",
1387 | "direction": "outbound-reply",
1388 | "api_version": "2010-04-01",
1389 | "price": "-0.00750",
1390 | "price_unit": "USD",
1391 | "error_code": null,
1392 | "error_message": null,
1393 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa4008eec5726061f3d58eb1f989f2497",
1394 | "subresource_uris": {
1395 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa4008eec5726061f3d58eb1f989f2497/Media.json"
1396 | },
1397 | "dateCreated": "2015-02-09T23:09:46.000Z",
1398 | "dateUpdated": "2015-02-09T23:09:47.000Z",
1399 | "dateSent": "2015-02-09T23:09:46.000Z",
1400 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1401 | "messagingServiceSid": null,
1402 | "numSegments": "1",
1403 | "numMedia": "0",
1404 | "apiVersion": "2010-04-01",
1405 | "priceUnit": "USD",
1406 | "errorCode": null,
1407 | "errorMessage": null,
1408 | "subresourceUris": {
1409 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa4008eec5726061f3d58eb1f989f2497/Media.json"
1410 | }
1411 | },
1412 | {
1413 | "sid": "SM6ff133e507fe4c1350dc2b9c2d627f05",
1414 | "date_created": "Mon, 09 Feb 2015 23:09:38 +0000",
1415 | "date_updated": "Mon, 09 Feb 2015 23:09:46 +0000",
1416 | "date_sent": "Mon, 09 Feb 2015 23:09:46 +0000",
1417 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1418 | "to": "+15851427131",
1419 | "from": "+12597150948",
1420 | "messaging_service_sid": null,
1421 | "body": "Non elit deserunt velit cupidatat magna est cillum.",
1422 | "status": "received",
1423 | "num_segments": "1",
1424 | "num_media": "0",
1425 | "direction": "inbound",
1426 | "api_version": "2010-04-01",
1427 | "price": "-0.00750",
1428 | "price_unit": "USD",
1429 | "error_code": null,
1430 | "error_message": null,
1431 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM6ff133e507fe4c1350dc2b9c2d627f05",
1432 | "subresource_uris": {
1433 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM6ff133e507fe4c1350dc2b9c2d627f05/Media.json"
1434 | },
1435 | "dateCreated": "2015-02-09T23:09:38.000Z",
1436 | "dateUpdated": "2015-02-09T23:09:46.000Z",
1437 | "dateSent": "2015-02-09T23:09:46.000Z",
1438 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1439 | "messagingServiceSid": null,
1440 | "numSegments": "1",
1441 | "numMedia": "0",
1442 | "apiVersion": "2010-04-01",
1443 | "priceUnit": "USD",
1444 | "errorCode": null,
1445 | "errorMessage": null,
1446 | "subresourceUris": {
1447 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM6ff133e507fe4c1350dc2b9c2d627f05/Media.json"
1448 | }
1449 | },
1450 | {
1451 | "sid": "SMa15bc0445c7e3a15afd7365b66932a6b",
1452 | "date_created": "Mon, 09 Feb 2015 20:12:22 +0000",
1453 | "date_updated": "Mon, 09 Feb 2015 20:12:23 +0000",
1454 | "date_sent": "Mon, 09 Feb 2015 20:12:22 +0000",
1455 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1456 | "to": "+17034524023",
1457 | "from": "+15851427131",
1458 | "messaging_service_sid": null,
1459 | "body": "Excepteur eu ea fugiat incididunt.",
1460 | "status": "delivered",
1461 | "num_segments": "1",
1462 | "num_media": "0",
1463 | "direction": "outbound-reply",
1464 | "api_version": "2010-04-01",
1465 | "price": "-0.00750",
1466 | "price_unit": "USD",
1467 | "error_code": null,
1468 | "error_message": null,
1469 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa15bc0445c7e3a15afd7365b66932a6b",
1470 | "subresource_uris": {
1471 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa15bc0445c7e3a15afd7365b66932a6b/Media.json"
1472 | },
1473 | "dateCreated": "2015-02-09T20:12:22.000Z",
1474 | "dateUpdated": "2015-02-09T20:12:23.000Z",
1475 | "dateSent": "2015-02-09T20:12:22.000Z",
1476 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1477 | "messagingServiceSid": null,
1478 | "numSegments": "1",
1479 | "numMedia": "0",
1480 | "apiVersion": "2010-04-01",
1481 | "priceUnit": "USD",
1482 | "errorCode": null,
1483 | "errorMessage": null,
1484 | "subresourceUris": {
1485 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa15bc0445c7e3a15afd7365b66932a6b/Media.json"
1486 | }
1487 | },
1488 | {
1489 | "sid": "SM36ba30e62c727a91f54e7d69dca955ca",
1490 | "date_created": "Mon, 09 Feb 2015 20:12:19 +0000",
1491 | "date_updated": "Mon, 09 Feb 2015 20:12:22 +0000",
1492 | "date_sent": "Mon, 09 Feb 2015 20:12:22 +0000",
1493 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1494 | "to": "+15851427131",
1495 | "from": "+17034524023",
1496 | "messaging_service_sid": null,
1497 | "body": "Do veniam fugiat irure culpa voluptate ea sunt nisi irure.",
1498 | "status": "received",
1499 | "num_segments": "1",
1500 | "num_media": "0",
1501 | "direction": "inbound",
1502 | "api_version": "2010-04-01",
1503 | "price": "-0.00750",
1504 | "price_unit": "USD",
1505 | "error_code": null,
1506 | "error_message": null,
1507 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM36ba30e62c727a91f54e7d69dca955ca",
1508 | "subresource_uris": {
1509 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM36ba30e62c727a91f54e7d69dca955ca/Media.json"
1510 | },
1511 | "dateCreated": "2015-02-09T20:12:19.000Z",
1512 | "dateUpdated": "2015-02-09T20:12:22.000Z",
1513 | "dateSent": "2015-02-09T20:12:22.000Z",
1514 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1515 | "messagingServiceSid": null,
1516 | "numSegments": "1",
1517 | "numMedia": "0",
1518 | "apiVersion": "2010-04-01",
1519 | "priceUnit": "USD",
1520 | "errorCode": null,
1521 | "errorMessage": null,
1522 | "subresourceUris": {
1523 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM36ba30e62c727a91f54e7d69dca955ca/Media.json"
1524 | }
1525 | },
1526 | {
1527 | "sid": "SM03278d5d477b8511c90038bb271b6c43",
1528 | "date_created": "Mon, 09 Feb 2015 20:04:05 +0000",
1529 | "date_updated": "Mon, 09 Feb 2015 20:04:06 +0000",
1530 | "date_sent": "Mon, 09 Feb 2015 20:04:05 +0000",
1531 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1532 | "to": "+11628737261",
1533 | "from": "+15851427131",
1534 | "messaging_service_sid": null,
1535 | "body": "Ut consequat esse et nisi esse.",
1536 | "status": "delivered",
1537 | "num_segments": "1",
1538 | "num_media": "0",
1539 | "direction": "outbound-reply",
1540 | "api_version": "2010-04-01",
1541 | "price": "-0.00750",
1542 | "price_unit": "USD",
1543 | "error_code": null,
1544 | "error_message": null,
1545 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM03278d5d477b8511c90038bb271b6c43",
1546 | "subresource_uris": {
1547 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM03278d5d477b8511c90038bb271b6c43/Media.json"
1548 | },
1549 | "dateCreated": "2015-02-09T20:04:05.000Z",
1550 | "dateUpdated": "2015-02-09T20:04:06.000Z",
1551 | "dateSent": "2015-02-09T20:04:05.000Z",
1552 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1553 | "messagingServiceSid": null,
1554 | "numSegments": "1",
1555 | "numMedia": "0",
1556 | "apiVersion": "2010-04-01",
1557 | "priceUnit": "USD",
1558 | "errorCode": null,
1559 | "errorMessage": null,
1560 | "subresourceUris": {
1561 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM03278d5d477b8511c90038bb271b6c43/Media.json"
1562 | }
1563 | },
1564 | {
1565 | "sid": "SM2cafefdd022c5a8ace7c828a21e2b53d",
1566 | "date_created": "Mon, 09 Feb 2015 20:04:05 +0000",
1567 | "date_updated": "Mon, 09 Feb 2015 20:04:05 +0000",
1568 | "date_sent": "Mon, 09 Feb 2015 20:04:05 +0000",
1569 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1570 | "to": "+15851427131",
1571 | "from": "+11628737261",
1572 | "messaging_service_sid": null,
1573 | "body": "Id tempor elit esse esse sint ullamco Lorem in est.",
1574 | "status": "received",
1575 | "num_segments": "1",
1576 | "num_media": "0",
1577 | "direction": "inbound",
1578 | "api_version": "2010-04-01",
1579 | "price": "-0.00750",
1580 | "price_unit": "USD",
1581 | "error_code": null,
1582 | "error_message": null,
1583 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2cafefdd022c5a8ace7c828a21e2b53d",
1584 | "subresource_uris": {
1585 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2cafefdd022c5a8ace7c828a21e2b53d/Media.json"
1586 | },
1587 | "dateCreated": "2015-02-09T20:04:05.000Z",
1588 | "dateUpdated": "2015-02-09T20:04:05.000Z",
1589 | "dateSent": "2015-02-09T20:04:05.000Z",
1590 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1591 | "messagingServiceSid": null,
1592 | "numSegments": "1",
1593 | "numMedia": "0",
1594 | "apiVersion": "2010-04-01",
1595 | "priceUnit": "USD",
1596 | "errorCode": null,
1597 | "errorMessage": null,
1598 | "subresourceUris": {
1599 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM2cafefdd022c5a8ace7c828a21e2b53d/Media.json"
1600 | }
1601 | },
1602 | {
1603 | "sid": "SMef0e90c171ee7e81ac53c80876537cd2",
1604 | "date_created": "Mon, 09 Feb 2015 20:03:55 +0000",
1605 | "date_updated": "Mon, 09 Feb 2015 20:03:56 +0000",
1606 | "date_sent": "Mon, 09 Feb 2015 20:03:55 +0000",
1607 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1608 | "to": "+11628737261",
1609 | "from": "+15851427131",
1610 | "messaging_service_sid": null,
1611 | "body": "Exercitation occaecat mollit proident ea cillum cillum cupidatat voluptate commodo pariatur Lorem sint.",
1612 | "status": "delivered",
1613 | "num_segments": "1",
1614 | "num_media": "0",
1615 | "direction": "outbound-reply",
1616 | "api_version": "2010-04-01",
1617 | "price": "-0.00750",
1618 | "price_unit": "USD",
1619 | "error_code": null,
1620 | "error_message": null,
1621 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMef0e90c171ee7e81ac53c80876537cd2",
1622 | "subresource_uris": {
1623 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMef0e90c171ee7e81ac53c80876537cd2/Media.json"
1624 | },
1625 | "dateCreated": "2015-02-09T20:03:55.000Z",
1626 | "dateUpdated": "2015-02-09T20:03:56.000Z",
1627 | "dateSent": "2015-02-09T20:03:55.000Z",
1628 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1629 | "messagingServiceSid": null,
1630 | "numSegments": "1",
1631 | "numMedia": "0",
1632 | "apiVersion": "2010-04-01",
1633 | "priceUnit": "USD",
1634 | "errorCode": null,
1635 | "errorMessage": null,
1636 | "subresourceUris": {
1637 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMef0e90c171ee7e81ac53c80876537cd2/Media.json"
1638 | }
1639 | },
1640 | {
1641 | "sid": "SMa7a099d63fde9e54163253a96a162182",
1642 | "date_created": "Mon, 09 Feb 2015 20:03:54 +0000",
1643 | "date_updated": "Mon, 09 Feb 2015 20:03:55 +0000",
1644 | "date_sent": "Mon, 09 Feb 2015 20:03:55 +0000",
1645 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1646 | "to": "+15851427131",
1647 | "from": "+11628737261",
1648 | "messaging_service_sid": null,
1649 | "body": "Quis eu do pariatur sunt quis proident non irure amet mollit non culpa cillum aliquip.",
1650 | "status": "received",
1651 | "num_segments": "1",
1652 | "num_media": "0",
1653 | "direction": "inbound",
1654 | "api_version": "2010-04-01",
1655 | "price": "-0.00750",
1656 | "price_unit": "USD",
1657 | "error_code": null,
1658 | "error_message": null,
1659 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa7a099d63fde9e54163253a96a162182",
1660 | "subresource_uris": {
1661 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa7a099d63fde9e54163253a96a162182/Media.json"
1662 | },
1663 | "dateCreated": "2015-02-09T20:03:54.000Z",
1664 | "dateUpdated": "2015-02-09T20:03:55.000Z",
1665 | "dateSent": "2015-02-09T20:03:55.000Z",
1666 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1667 | "messagingServiceSid": null,
1668 | "numSegments": "1",
1669 | "numMedia": "0",
1670 | "apiVersion": "2010-04-01",
1671 | "priceUnit": "USD",
1672 | "errorCode": null,
1673 | "errorMessage": null,
1674 | "subresourceUris": {
1675 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SMa7a099d63fde9e54163253a96a162182/Media.json"
1676 | }
1677 | },
1678 | {
1679 | "sid": "SM7eecbad339332a1e0c8eee7553e6e0b9",
1680 | "date_created": "Mon, 09 Feb 2015 20:02:15 +0000",
1681 | "date_updated": "Mon, 09 Feb 2015 20:02:16 +0000",
1682 | "date_sent": "Mon, 09 Feb 2015 20:02:15 +0000",
1683 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1684 | "to": "+17668017655",
1685 | "from": "+15851427131",
1686 | "messaging_service_sid": null,
1687 | "body": "Ex aliquip irure dolore proident occaecat adipisicing minim adipisicing commodo adipisicing adipisicing anim.",
1688 | "status": "delivered",
1689 | "num_segments": "1",
1690 | "num_media": "0",
1691 | "direction": "outbound-reply",
1692 | "api_version": "2010-04-01",
1693 | "price": "-0.00750",
1694 | "price_unit": "USD",
1695 | "error_code": null,
1696 | "error_message": null,
1697 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7eecbad339332a1e0c8eee7553e6e0b9",
1698 | "subresource_uris": {
1699 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7eecbad339332a1e0c8eee7553e6e0b9/Media.json"
1700 | },
1701 | "dateCreated": "2015-02-09T20:02:15.000Z",
1702 | "dateUpdated": "2015-02-09T20:02:16.000Z",
1703 | "dateSent": "2015-02-09T20:02:15.000Z",
1704 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1705 | "messagingServiceSid": null,
1706 | "numSegments": "1",
1707 | "numMedia": "0",
1708 | "apiVersion": "2010-04-01",
1709 | "priceUnit": "USD",
1710 | "errorCode": null,
1711 | "errorMessage": null,
1712 | "subresourceUris": {
1713 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7eecbad339332a1e0c8eee7553e6e0b9/Media.json"
1714 | }
1715 | },
1716 | {
1717 | "sid": "SM517dafa5d096f4343e897b777508bd7a",
1718 | "date_created": "Mon, 09 Feb 2015 20:02:14 +0000",
1719 | "date_updated": "Mon, 09 Feb 2015 20:02:15 +0000",
1720 | "date_sent": "Mon, 09 Feb 2015 20:02:15 +0000",
1721 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1722 | "to": "+15851427131",
1723 | "from": "+17668017655",
1724 | "messaging_service_sid": null,
1725 | "body": "Sunt aute pariatur occaecat in cillum eu proident consectetur eiusmod fugiat reprehenderit est et cillum.",
1726 | "status": "received",
1727 | "num_segments": "1",
1728 | "num_media": "0",
1729 | "direction": "inbound",
1730 | "api_version": "2010-04-01",
1731 | "price": "-0.00750",
1732 | "price_unit": "USD",
1733 | "error_code": null,
1734 | "error_message": null,
1735 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM517dafa5d096f4343e897b777508bd7a",
1736 | "subresource_uris": {
1737 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM517dafa5d096f4343e897b777508bd7a/Media.json"
1738 | },
1739 | "dateCreated": "2015-02-09T20:02:14.000Z",
1740 | "dateUpdated": "2015-02-09T20:02:15.000Z",
1741 | "dateSent": "2015-02-09T20:02:15.000Z",
1742 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1743 | "messagingServiceSid": null,
1744 | "numSegments": "1",
1745 | "numMedia": "0",
1746 | "apiVersion": "2010-04-01",
1747 | "priceUnit": "USD",
1748 | "errorCode": null,
1749 | "errorMessage": null,
1750 | "subresourceUris": {
1751 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM517dafa5d096f4343e897b777508bd7a/Media.json"
1752 | }
1753 | },
1754 | {
1755 | "sid": "SM63d1c66549aef33b2918aaea83a6e869",
1756 | "date_created": "Mon, 09 Feb 2015 19:59:25 +0000",
1757 | "date_updated": "Mon, 09 Feb 2015 19:59:25 +0000",
1758 | "date_sent": "Mon, 09 Feb 2015 19:59:25 +0000",
1759 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1760 | "to": "+12597150948",
1761 | "from": "+15851427131",
1762 | "messaging_service_sid": null,
1763 | "body": "Exercitation commodo reprehenderit consequat deserunt veniam commodo irure.",
1764 | "status": "delivered",
1765 | "num_segments": "1",
1766 | "num_media": "0",
1767 | "direction": "outbound-reply",
1768 | "api_version": "2010-04-01",
1769 | "price": "-0.00750",
1770 | "price_unit": "USD",
1771 | "error_code": null,
1772 | "error_message": null,
1773 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM63d1c66549aef33b2918aaea83a6e869",
1774 | "subresource_uris": {
1775 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM63d1c66549aef33b2918aaea83a6e869/Media.json"
1776 | },
1777 | "dateCreated": "2015-02-09T19:59:25.000Z",
1778 | "dateUpdated": "2015-02-09T19:59:25.000Z",
1779 | "dateSent": "2015-02-09T19:59:25.000Z",
1780 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1781 | "messagingServiceSid": null,
1782 | "numSegments": "1",
1783 | "numMedia": "0",
1784 | "apiVersion": "2010-04-01",
1785 | "priceUnit": "USD",
1786 | "errorCode": null,
1787 | "errorMessage": null,
1788 | "subresourceUris": {
1789 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM63d1c66549aef33b2918aaea83a6e869/Media.json"
1790 | }
1791 | },
1792 | {
1793 | "sid": "SM7d99deee5fbc9162dcac9c705e0b19dc",
1794 | "date_created": "Mon, 09 Feb 2015 19:59:24 +0000",
1795 | "date_updated": "Mon, 09 Feb 2015 19:59:25 +0000",
1796 | "date_sent": "Mon, 09 Feb 2015 19:59:25 +0000",
1797 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1798 | "to": "+15851427131",
1799 | "from": "+12597150948",
1800 | "messaging_service_sid": null,
1801 | "body": "Reprehenderit consequat exercitation proident ullamco commodo aute sunt ipsum excepteur.",
1802 | "status": "received",
1803 | "num_segments": "1",
1804 | "num_media": "0",
1805 | "direction": "inbound",
1806 | "api_version": "2010-04-01",
1807 | "price": "-0.00750",
1808 | "price_unit": "USD",
1809 | "error_code": null,
1810 | "error_message": null,
1811 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7d99deee5fbc9162dcac9c705e0b19dc",
1812 | "subresource_uris": {
1813 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7d99deee5fbc9162dcac9c705e0b19dc/Media.json"
1814 | },
1815 | "dateCreated": "2015-02-09T19:59:24.000Z",
1816 | "dateUpdated": "2015-02-09T19:59:25.000Z",
1817 | "dateSent": "2015-02-09T19:59:25.000Z",
1818 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1819 | "messagingServiceSid": null,
1820 | "numSegments": "1",
1821 | "numMedia": "0",
1822 | "apiVersion": "2010-04-01",
1823 | "priceUnit": "USD",
1824 | "errorCode": null,
1825 | "errorMessage": null,
1826 | "subresourceUris": {
1827 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM7d99deee5fbc9162dcac9c705e0b19dc/Media.json"
1828 | }
1829 | },
1830 | {
1831 | "sid": "SM69eaaa68f1a4291986f18adf615fbcf1",
1832 | "date_created": "Mon, 09 Feb 2015 19:44:04 +0000",
1833 | "date_updated": "Mon, 09 Feb 2015 19:44:04 +0000",
1834 | "date_sent": "Mon, 09 Feb 2015 19:44:04 +0000",
1835 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1836 | "to": "+13088632703",
1837 | "from": "+15851427131",
1838 | "messaging_service_sid": null,
1839 | "body": "Non id reprehenderit in culpa cupidatat laborum cillum ipsum.",
1840 | "status": "delivered",
1841 | "num_segments": "1",
1842 | "num_media": "0",
1843 | "direction": "outbound-reply",
1844 | "api_version": "2010-04-01",
1845 | "price": "-0.00750",
1846 | "price_unit": "USD",
1847 | "error_code": null,
1848 | "error_message": null,
1849 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM69eaaa68f1a4291986f18adf615fbcf1",
1850 | "subresource_uris": {
1851 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM69eaaa68f1a4291986f18adf615fbcf1/Media.json"
1852 | },
1853 | "dateCreated": "2015-02-09T19:44:04.000Z",
1854 | "dateUpdated": "2015-02-09T19:44:04.000Z",
1855 | "dateSent": "2015-02-09T19:44:04.000Z",
1856 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1857 | "messagingServiceSid": null,
1858 | "numSegments": "1",
1859 | "numMedia": "0",
1860 | "apiVersion": "2010-04-01",
1861 | "priceUnit": "USD",
1862 | "errorCode": null,
1863 | "errorMessage": null,
1864 | "subresourceUris": {
1865 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM69eaaa68f1a4291986f18adf615fbcf1/Media.json"
1866 | }
1867 | },
1868 | {
1869 | "sid": "SM10188adc98e57da4128e4b5318ae4c82",
1870 | "date_created": "Mon, 09 Feb 2015 19:44:03 +0000",
1871 | "date_updated": "Mon, 09 Feb 2015 19:44:04 +0000",
1872 | "date_sent": "Mon, 09 Feb 2015 19:44:04 +0000",
1873 | "account_sid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1874 | "to": "+15851427131",
1875 | "from": "+13088632703",
1876 | "messaging_service_sid": null,
1877 | "body": "Amet ullamco Lorem est veniam occaecat ex duis occaecat.",
1878 | "status": "received",
1879 | "num_segments": "1",
1880 | "num_media": "0",
1881 | "direction": "inbound",
1882 | "api_version": "2010-04-01",
1883 | "price": "-0.00750",
1884 | "price_unit": "USD",
1885 | "error_code": null,
1886 | "error_message": null,
1887 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM10188adc98e57da4128e4b5318ae4c82",
1888 | "subresource_uris": {
1889 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM10188adc98e57da4128e4b5318ae4c82/Media.json"
1890 | },
1891 | "dateCreated": "2015-02-09T19:44:03.000Z",
1892 | "dateUpdated": "2015-02-09T19:44:04.000Z",
1893 | "dateSent": "2015-02-09T19:44:04.000Z",
1894 | "accountSid": "ACf3072d52657d8f496d1e8c1c30e74b39",
1895 | "messagingServiceSid": null,
1896 | "numSegments": "1",
1897 | "numMedia": "0",
1898 | "apiVersion": "2010-04-01",
1899 | "priceUnit": "USD",
1900 | "errorCode": null,
1901 | "errorMessage": null,
1902 | "subresourceUris": {
1903 | "media": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages/SM10188adc98e57da4128e4b5318ae4c82/Media.json"
1904 | }
1905 | }
1906 | ],
1907 | "uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages.json?PageSize=50&Page=0",
1908 | "page_size": 50,
1909 | "start": 0,
1910 | "next_page_uri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages.json?PageSize=50&Page=1&PageToken=PAa60e37541ee118ba53afb39dc129201d",
1911 | "page": 0,
1912 | "firstPageUri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages.json?PageSize=50&Page=0",
1913 | "previousPageUri": null,
1914 | "pageSize": 50,
1915 | "nextPageUri": "/2010-04-01/Accounts/ACf3072d52657d8f496d1e8c1c30e74b39/Messages.json?PageSize=50&Page=1&PageToken=PAa60e37541ee118ba53afb39dc129201d"
1916 | }
1917 |
--------------------------------------------------------------------------------