Getting Started
Overview
This guide shows how to connect a service to Bushel’s live data feed. The data feed consists of an initial historical load of a Users' data in addition to near-real-time updates to new or existing records.
Bushel’s Connection Service manages the authorization of producers' data sent to 3rd party services. Users must opt-in to sharing their data outside of Bushel and remain in control over what data is shared.
Workflow
A User initiates a connection from Bushel to your integration with the following steps:
-
The User clicks a link redirecting them to Bushel’s Connection Service
-
If not already logged in, the User is prompted to login to their Bushel account
-
The User selects and confirms the account(s) to add to the connection
-
The User is redirected back to the originating web application
-
The Connection service will send historical and real-time data for the selected accounts to the pre-configured Endpoints
Getting Started
Prerequisites
In order to create an integration with the Connection Service, you will need the following:
- consumer_id
-
A unique identifier supplied by Bushel for your integration.
- consumer_secret
-
A secret key supplied by Bushel used to sign requests and authenticate data received. This must remain secret and must not be exposed to clients.
- Connection Endpoint(s)
-
One or more URL endpoints that will receive data sent by Bushel.
Creating a Link to the Connection Service
Users begin the process of creating connections by clicking a link directing them to to Bushel’s Connection Service. The link must include a JSON Web Token (JWT) containing claims that uniquely identify the user in your system. The URL that a User will click to begin the connection process is:
https://connections.bushelops.com/connect?consumerJWT={jwt}
In this example, {jwt}
should be replaced by a JWT crafted by your application specifically for the User. JWTs consist of three Base64 encoded sections, separated by periods (.
): the Header, Payload, and Signature. Let’s create a JWT that will let Farmer Fran create connections to an integration.
Header
The JWT header includes meta-information about the JWT. This JWT must be signed using the HMAC-SHA256 algorithm using a secret key provided by Bushel. As such, the Header (prior to base64 encoding) of the JWT should be:
{
"alg": "HS256",
"typ": "JWT"
}
Payload
The Payload of the JWT contains identifiers for the User in your system in addition to information that make the Users connection experience as easy and intuitive as possible. An example payload (prior to base64 encoding) is as follows:
{
"cancelUrl": "https://example.com/account-management", (1)
"consumerId": "dbfba6ce-40a7-45e8-9807-ac91a4132d63", (2)
"consumerTargetId": "FxB7htYrqh0RAylX", (3)
"consumerTargetName": "Farmer Fran", (4)
"disclaimer": "By connecting to your accounts with grain buyers in the Bushel network, you are giving permission for data from these accounts to feed into your Bushel Farm account with [email protected].", (5)
"exp": 1722023598, (6)
"iat": 1722023478, (7)
"redirectUrl": "https://example.com/integrations/bushel/callback" (8)
}
1 | The URL in your application the User should be redirected to if they Cancel the Subscription flow (required) |
2 | An identifier provided by Bushel specific to your integration (required) |
3 | A unique identifier for the User in your integration. Bushel will return this ID on all data for the User sent to your integration’s Endpoints (required) |
4 | The user-friendly name of the account or profile of the User in your system (required) |
5 | The disclaimer to be displayed to the User by Bushel’s Connection Service when prompted to set up subscriptions (required) |
6 | The expiration time in seconds from the Unix epoch of this JWT. This should be a relatively short amount of time (e.g. 5 minutes from the current time) (required) |
7 | The time at which this JWT was created, measured in seconds from the Unix epoch (optional) |
8 | The URL the User is redirected to once they have successfully completed the subscription process (required) |
Signature
The signature of the JWT is created by calculating the HMAC-SHA256 of the base64 encoded values of the header and payload, separated by a period (.
), and signed with the consumer_secret
provided by Bushel.
HMACSHA256(
base64Encode(header) + "." + base64Encode(payload),
consumer_secret
)
The consumer_secret is a confidential secret used to authenticate and verify data sent to Bushel and received by your Endpoints. It must never be exposed to clients (e.g. web browsers).
|
Using a consumer_secret
of my-secret-key
, the JWT of the above header and payload is as follows.
Base64 encoding the header and payload and using a consumer_secret
of my-secret-key
results in a HMAC-SHA256 of:
nWRjhGWh2hGpmq_53U-QPEPx-jYJBOG50WrfuxUUIZ8
Final JWT
By concatenating the base64 encoded header, base64 encoded payload, and signature with periods between each element, we arrive at the final JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYW5jZWxVcmwiOiJodHRwczovL2V4YW1wbGUuY29tL2FjY291bnQtbWFuYWdlbWVudCIsImNvbnN1bWVySWQiOiJkYmZiYTZjZS00MGE3LTQ1ZTgtOTgwNy1hYzkxYTQxMzJkNjMiLCJjb25zdW1lclRhcmdldElkIjoiRnhCN2h0WXJxaDBSQXlsWCIsImNvbnN1bWVyVGFyZ2V0TmFtZSI6IkZhcm1lciBGcmFuIiwiZGlzY2xhaW1lciI6IkJ5IGNvbm5lY3RpbmcgdG8geW91ciBhY2NvdW50cyB3aXRoIGdyYWluIGJ1eWVycyBpbiB0aGUgQnVzaGVsIG5ldHdvcmssIHlvdSBhcmUgZ2l2aW5nIHBlcm1pc3Npb24gZm9yIGRhdGEgZnJvbSB0aGVzZSBhY2NvdW50cyB0byBmZWVkIGludG8geW91ciBCdXNoZWwgRmFybSBhY2NvdW50IHdpdGggZmFybWVyZnJhbkBleGFtcGxlLmNvbS4iLCJleHAiOjE3MjIwMjM1OTgsImlhdCI6MTcyMjAyMzQ3OCwicmVkaXJlY3RVcmwiOiJodHRwczovL2V4YW1wbGUuY29tL2ludGVncmF0aW9ucy9idXNoZWwvY2FsbGJhY2sifQ.nWRjhGWh2hGpmq_53U-QPEPx-jYJBOG50WrfuxUUIZ8
An interactive version of this token is available on jwt.io (be sure to replace the signature key with my-secret-key
).
This JWT can now be used to create a link that directs the User to Bushel’s Connection Service.
https://connections.bushelops.com/connect?consumerJWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYW5jZWxVcmwiOiJodHRwczovL2V4YW1wbGUuY29tL2FjY291bnQtbWFuYWdlbWVudCIsImNvbnN1bWVySWQiOiJkYmZiYTZjZS00MGE3LTQ1ZTgtOTgwNy1hYzkxYTQxMzJkNjMiLCJjb25zdW1lclRhcmdldElkIjoiRnhCN2h0WXJxaDBSQXlsWCIsImNvbnN1bWVyVGFyZ2V0TmFtZSI6IkZhcm1lciBGcmFuIiwiZGlzY2xhaW1lciI6IkJ5IGNvbm5lY3RpbmcgdG8geW91ciBhY2NvdW50cyB3aXRoIGdyYWluIGJ1eWVycyBpbiB0aGUgQnVzaGVsIG5ldHdvcmssIHlvdSBhcmUgZ2l2aW5nIHBlcm1pc3Npb24gZm9yIGRhdGEgZnJvbSB0aGVzZSBhY2NvdW50cyB0byBmZWVkIGludG8geW91ciBCdXNoZWwgRmFybSBhY2NvdW50IHdpdGggZmFybWVyZnJhbkBleGFtcGxlLmNvbS4iLCJleHAiOjE3MjIwMjM1OTgsImlhdCI6MTcyMjAyMzQ3OCwicmVkaXJlY3RVcmwiOiJodHRwczovL2V4YW1wbGUuY29tL2ludGVncmF0aW9ucy9idXNoZWwvY2FsbGJhY2sifQ.nWRjhGWh2hGpmq_53U-QPEPx-jYJBOG50WrfuxUUIZ8
User Registration
Once the User clicks the registration link created above, they are redirected to Bushel’s Connection Service where they go through the Connection flow. The User selects the account(s) at one or more companies that they would like data shared for. After confirming their selections, the user is redirected back to the URL provided as the redirectUrl
in the JWT. More information about the User’s experience is available at User Registration Flow.
Creating Endpoints
Endpoints are the URLs where the data feeds are sent to your integration. These URLs may be the same for all or unique to each object type. Once you have determined the URLs for your Endpoints, Bushel will configure your integration’s data feed to send each object type to its respective Endpoint.
Data is sent to these Endpoints as JSON-encoded HTTP POST requests whenever it is created, deleted, or modified in Bushel’s system. In addition in incremental updates, an initial historical load of all existing data is sent whenever a new connection is created.
The latest API spec for data sent to the endpoints is available at our Connection API Reference.
Validating Pushes
Data sent to Endpoints is cryptographically signed. The signature is provided in the WebhookSignature
header.
WebhookSignature: timestamp=1722453916,signature=64f025f73812f91a970bd8153ef81216d3f05232b2d0c02780b6504d94fc8017
The header contains the timestamp and signature generated by Bushel’s servers. These values must be used to verify the authenticity of the request and ensure that it originated from Bushel.
The timestamp
is the Unix time on Bushel’s servers when the request was generated. This value is used when calculating the signature in order to verify the authenticity of the request. In addition, the timestamp must be verified to ensure that is resonably close to that of the receiving servers time in order to prevent replay attacks.
The signature
portion of the header is the base64 encoded HMAC-SHA256 signature of the request. The signature is calculated by calculating the HMAC-SHA256 of the timestamp, a hyphen, and the JSON request body signed with the consumerSecret given when setting up the integration with Bushel.
For example, given a request with the WebhookSignature
and body of the following:
WebhookSignature: timestamp=1722456802,signature=3ffb359361bc15c65941653407074693d72b5862dbc259be351967bd0d813652
{"feature":"tenant","data":[{"connectionId":"96fa3e3d-39d6-4d65-80a2-e0a989f56332","consumerId":"53ea9b09-13bd-4bfb-bcdf-eb942d117673","tenant":{"id":"46c8b1cd-767c-4eb0-b8dc-e07b4f82d2c8","name":"Bushel Sales Demo","createdAt":"2023-01-24T14:15:22Z","updatedAt":"2023-01-24T14:15:22Z","deletedAt":null}}]}
First, verify that the timestamp (1722457271
or 2024-07-31T08:23:34Z
in RFC-3339 format) is within a few minutes of the receiving servers own time. If it is not, the request may be a fradulent replay attack and should be rejected and not processed.
Next, calculate the signature of the request and compare against the signature provided in the WebhookSignature
header. If the signatures do not match, the request must not be trusted and must be rejected and not processed. The signature is created by calculating the HMAC-SHA256 of the timestamp, a hyphen, and the JSON request body signed with the consumerSecret
. In our example, the signature is calculated by the following pseudo-code:
stringToSign = 1722456802-{"feature":"tenant","data":[{"connectionId":"96fa3e3d-39d6-4d65-80a2-e0a989f56332","consumerId":"53ea9b09-13bd-4bfb-bcdf-eb942d117673","tenant":{"id":"46c8b1cd-767c-4eb0-b8dc-e07b4f82d2c8","name":"Bushel Sales Demo","createdAt":"2023-01-24T14:15:22Z","updatedAt":"2023-01-24T14:15:22Z","deletedAt":null}}]}
hmacSignature = hmac256('my-secret-key', stringToSign)
signature = Base64Encode(hmacSignature)
This results in a signature of 3ffb359361bc15c65941653407074693d72b5862dbc259be351967bd0d813652
. Since this is identical to the signature provided by by the signature
in the WebhookSignature
header, we know the request originated from Bushel and can be trusted.