mass-contact text/SMS distribution tool
Switch branches/tags
3-texter-receives-text-that-list-is-ready 141-bug-fix-release 148-polling 148-qrs-polling 182-review-interface 317-ping-command 349-dev-dependencies 382-cypress-tests 434-buttons 496-zip-smalldb 550-add-redis-modules 554-start-campaign-redis 577-no-opt-out-msg 594-suppress-ak-email-signup 606-revere-v2 610-e2e-saucelabs 617-e2e-edit-user 670-campaign-editor-presence 709-assignment-bug 711-skipped-passed-msg-bug 741-also-invalidate-org-cache 741-default-instancewide-optout-message 762-supervolunteers-access-update 767-no-editing-script 775-iphone5-readability 779-killing-jobs 786-reverse-order-past-messages 791-sendMessage-in-cacheable-queries 796-role-expiration 798-response-sort-scaling 798-response-sort 799-admin-campaign-list-loading 813-decouple-contactgetting-moredocs 813-decouple-contactgetting 813-firsttextfromcampaign 813-usecount-justgetcount 817-sqlitefix 828-fix-maxContacts-tests 828-fix-maxContacts 856-scaling-optouts 858-stop-texting-button 863-datawarehouse-imports-cell-formatting 875-visual-texts-assigned-not-sent 884-instance-opt-outs 894-user-org-cache-and-org-check 909-db-querying-v2 921-cache-question-response-tests 921-cache-question-response 934-orgless-user 978-update-role-in-cache accessrequired-await acct-for-opt-outs-for-unsent-msg-tag add-org-check-to-cache add-redis-docs add-saucelabs-link-to-repo add-security-details adds-job-for-backup-msg-queue2 assignment-test auto-deploy-stage az-updated-settings backend-tests beta-main bug-custom-fields-script cache-blocking cache-contexts cache-webhook camelcase-dependancy-missing campaign-page-related-fixes change-page-default-to-fifty clearloadercache community-guidelines cp4r3z-617-e2e-edit-user decoupling-avoidingqueries decoupling-clientside decoupling-contactinfo decoupling-prep default-us deletedupes-uploadquery distributed-warehouse-contacts-callback distributed-warehouse-contacts efficient-admincampaign enable-sqlitetests export-debug fix-absolute-url-error fix-akid-generation fix-cache-login fix-editors-presence-bug-scaling-messages fix-editors-presence-bug-stage-main fix-jest-config fix-orgcreatejoin fix-path-string-error fix-role-change fix-selfinvite fix-top-script fixing-lambda-deploy-docs-link force-display-of-graphql-errors jp-people-search jp-test-texter knex-jest knex-migrate-incoming-message-handler knex-migrations knex-rethink-ordertablecreation lambda-acceptspecialchartexts lambda-acceptspecialchartextstests lambdaassigntexters leaderboard-texters-all-time-query lint-branch47 lint-migrations local-login main-rollback main maxcontacts-nulldefault migrate-ifnotdbcreate mobile-ux-reg-622 modified-e2e-basic-texter new_texting_hours_per_campaign-withcaching new_texting_hours_per_campaign optouts-appearing-in-replies org-messageservice-migrate org-messageservice page-size-all pass-node-env-to-client people-pagination postknex-polish react-fixes react-v16 redis-change-file-structure redis-plan release-14 retrysend-once revert-345-revert-310-adds-job-for-backup-msg-queue revert-az-settings run-lint-on-main sampledata scaling-assignments scaling-cachedtodos scaling-campaign scaling-campaigncontacts2 scaling-cannedresponses scaling-dynamicassignment scaling-loaders scaling-messages-dyn scaling-messages scaling-optouts2 scaling-pluslarry scaling-streamcontacts scaling-tip-savefix scaling-tip scaling-wip simplify-getcontacts single-proces-model skip-auth0 sky-graphql-crazy sky-test171115 smallest-simultaneoususers stage-main testdata-in2timezones text-speed-debug-logs troubleshoot-prod tz-fix update-default-page-to-return-all update-scaling-doc update-travis-for-code-climate updatecontacts-afterdynassn user-cache-update ux-bug-blank-message warehouseconnect-opportunistic warehouseconnect-opportunisticmain wip-23-pool-number-refresh wip-pm2-for-dev
Nothing to show
Clone or download
schuyler1d Merge pull request #993 from MoveOnOrg/lambda-acceptspecialchartexts
accept twilio messages with special characters
Latest commit 6ee1b07 Oct 29, 2018
Type Name Latest commit message Commit time
Failed to load latest commit information.
__mocks__ tests config'd and basic db test working Jul 17, 2017
__test__ Fix e2e tests Oct 4, 2018
deploy Update lambda-env.json Aug 17, 2018
dev-tools cleaned up some unnecessary files and instructions related to debuggi… Sep 2, 2018
docs new job to add users to default org Oct 8, 2018
src opportunistically open the warehouse connection -- otherwise, dont co… Oct 16, 2018
webpack enabled inline source map Aug 12, 2018
.babelrc got rid of some changes we didn't need Jul 4, 2018
.env.example set DB_PORT=5432 May 9, 2018
.eslintrc Merge branch 'main' into cp4r3z-617-e2e-edit-user Aug 30, 2018
.gitignore adds beta files to gitignore Oct 10, 2018
.travis.yml Run e2e tests last Oct 4, 2018 a community guidelines link and statement (#657) Jun 19, 2018
Dockerfile Remove prod-build step from Dockerfile Aug 19, 2018
LICENSE Add Sheena to license May 17, 2017
Procfile Remove tunnel being used on production Sep 6, 2017 Add redis to docker-compose configuration Aug 19, 2018
app.json 539: removed AUTH0_LOGIN_CALLBACK and AUTH0_LOGOUT_CALLBACK env varia… Mar 30, 2018
docker-compose.yml Add redis to docker-compose configuration Aug 19, 2018
jest.config.e2e.js Add bail option to jest for e2e Oct 4, 2018
jest.config.js super long timeout to see if that's the prob on travis Sep 21, 2018
jest.config.sqlite.js Record test coverage. Nov 20, 2017
lambda.js accept twilio messages with special characters: a workaround from Twi… Oct 22, 2018
package.json Bumps rethink-knex-adapter version for batchSize option Oct 10, 2018
travis-run-e2e-tests E2E Tests - Use wait-on package to speed up CI Jul 22, 2018

Build Status

Sauce Test Status


Spoke is an open source text-distribution tool for organizations to mobilize supporters and members into action. Spoke allows you to upload phone numbers, customize scripts and assign volunteers to communicate with supporters while allowing organizations to manage the process.

Spoke was created by Saikat Chakrabarti and Sheena Pakanati, and is now maintained by

The latest version is 1.4.1 (see release notes) which we recommend for production use, while our main branch is where features still in development and testing will be available.


This is generated from react-apollo-starter-kit. Look at that project's README for info on some of the libraries used.

Deploy to Heroku


Follow up instructions located here

Please let us know if you deployed by filling out this form here

Getting started

  1. Install either sqlite (or another knex-supported database)
  2. Install the Node version listed under engines in package.json. NVM is one way to do this.
  3. npm install
  4. npm install -g foreman
  5. cp .env.example .env
  6. If you want to use Postgres:
    • In .env set DB_TYPE=pg. (Otherwise, you will use sqlite.)
    • Set DB_PORT=5432, which is the default port for Postgres.
    • Create the spokedev database: psql -c "create database spokedev;"
  7. Create an Auth0 account. In your Auth0 account, go to Applications, click on Default App and then grab your Client ID, Client Secret, and your Auth0 domain (should look like Add those inside your .env file (AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET, AUTH0_DOMAIN respectively).
  8. Run npm run dev to create and populate the tables.
  9. In your Auth0 app settings, add http://localhost:3000/login-callback , http://localhost:3000 and http://localhost:3000/logout-callback to "Allowed Callback URLs", "Allowed Web Origins" and "Allowed Logout URLs" respectively. (If you get an error when logging in later about "OIDC", go to Advanced Settings section, and then OAuth, and turn off 'OIDC Conformant')
  10. Add a new rule in Auth0:
function (user, context, callback) {
context.idToken["https://spoke/user_metadata"] = user.user_metadata;
callback(null, user, context);
  1. If the application is still running from step 8, kill the process and re-run npm run dev to restart the app. Wait until you see both "Node app is running ..." and "webpack: Compiled successfully." before attempting to connect. (make sure environment variable JOBS_SAME_PROCESS=1)
  2. Go to http://localhost:3000 to load the app.
  3. As long as you leave SUPPRESS_SELF_INVITE= blank and unset in your .env you should be able to invite yourself from the homepage.
    • If you DO set that variable, then spoke will be invite-only and you'll need to generate an invite. Run:
echo "INSERT INTO invite (hash,is_valid) VALUES ('abc', 1);" |sqlite3 mydb.sqlite
# Note: When doing this with PostgreSQL, you would replace the `1` with `true`
  • Then use the generated key to visit an invite link, e.g.: http://localhost:3000/invite/abc. This should redirect you to the login screen. Use the "Sign Up" option to create your account.
  1. You should then be prompted to create an organization. Create it.

If you want to create an invite via the home page "Login and get started" link, make sure your SUPPRESS_SELF_INVITE variable is not set.

Getting started with Docker

  1. cp .env.example .env
  2. Follow Steps 7, 9, & 10 above to set up your Auth0 account.
  3. Build a Spoke Docker image with docker-compose build app
  4. Start the PostgreSQL & Redis containers in the background with docker-compose up -d postgres redis.
  5. Start the Spoke application in the foreground with docker-compose up app.
  6. Go to http://localhost:3000 to load the app.
  7. Follow Step 13 above.
  • But if you need to generate an invite, run:
docker-compose exec postgres psql -U spoke -d spokedev -c "INSERT INTO invite (hash,is_valid) VALUES ('<your-hash>', true);"
  • Then use the generated key to visit an invite link, e.g.: http://localhost:3000/invite/<your-hash>. This should redirect you to the login screen. Use the "Sign Up" option to create your account.
  1. You should then be prompted to create an organization. Create it.
  2. Bring down your application with docker-compose down, or docker-compose down -v to bring it down and completely destroy your Postgres database & Redis datastore along with it.

Running Tests


Big Thanks

Cross-browser Testing Platform and Open Source <3 Provided by Sauce Labs.

Helpful Dev Tips


For development, you can set DEFAULT_SERVICE=fakeservice to skip using an SMS provider (Twilio or Nexmo) and insert the message directly into the database.

To simulate receiving a reply from a contact you can use the Send Replies utility: http://localhost:3000/admin/1/campaigns/1/send-replies, updating the app and campaign IDs as necessary.


Twilio provides test credentials that will not charge your account as described in their documentation. You may use either your test credentials or your live keys by following the instructions here.


  1. Run OUTPUT_DIR=./build npm run prod-build-server This will generate something you can deploy to production in ./build and run nodejs server/server/index.js
  2. Run npm run prod-build-client
  3. Make a copy of deploy/spoke-pm2.config.js.template, e.g. spoke-pm2.config.js, add missing environment variables, and run it with pm2, e.g. pm2 start spoke-pm2.config.js --env production
  4. Install PostgreSQL
  5. Start PostgreSQL (e.g. sudo /etc/init.d/postgresql start), connect (e.g. sudo -u postgres psql), create a user and database (e.g. create user spoke password 'spoke'; create database spoke owner spoke;), disconnect (e.g. \q) and add credentials to DB_ variables in spoke-pm2.config.js


Spoke is licensed under the MIT license.