├── .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 |
18 | 19 |
` 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 |
16 |

Login

17 |
18 | 22 | 23 | 27 | 28 | 29 | Forgot password 30 |
31 |
` 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` ${user.name}` 30 | : html`Login` 31 | 32 | return html` 33 |
34 | ${title} 35 | 40 |
` 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 |
17 | 21 | 22 | 23 | 24 | ${state.ui.resetPasswordInitSubmitted 25 | ? html`
An email has been sent to the address you entered with futher instructions.
` 26 | : ''} 27 |
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 | 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 |
18 | 22 | 23 | 27 | 28 | 29 | 30 | ${state.ui.resetPasswordConfirmSubmitted 31 | ? html`
Your password has been reset. Please Login.
` 32 | : ''} 33 |
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 |
41 | ${formatPhone(activePhone || '')} 42 |
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 |
42 | Conversations 43 | 44 | 48 |
` 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 | 59 | ${formatPhone(phone.label)} 60 | ${phone.unread > 0 ? html`${phone.unread}` : ''} 61 | 62 |
  • ` 63 | } 64 | 65 | function clickAdd (e) { 66 | if (onClickAdd) onClickAdd() 67 | e.preventDefault() 68 | } 69 | 70 | function addForm () { 71 | return html` 72 |
    73 | 74 |
    ` 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 |
    33 | 37 | 38 | 42 | 43 | 44 |
    45 | 46 | ${hasAdminAccess(state.user) 47 | ? html` 48 |
    49 |

    Invite new user

    50 |
    51 | 55 | 56 | 57 | 58 | ${state.ui.inviteSubmitted 59 | ? html`
    An email has been sent to the address you entered with futher instructions.
    ` 60 | : ''} 61 |
    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 | ![screenshot](http://i.imgur.com/PIRyygi.png) 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 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](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 | --------------------------------------------------------------------------------