Events
Asynchronous interactions between InvestSuite and the Client Middleware can happen in multiple ways, depending on the direction. The below table summarizes the capabilities:
Inbound InvestSuite | Outbound InvestSuite | |
---|---|---|
Event Driven | Not supported | The Client Middleware responds to an event that is sent by the InvestSuite system |
REST /events/ Endpoint |
The Client Middleware notifies InvestSuite of an event through the POST /events/ endpoint |
Under development The Client Middleware regularly polls the GET /events/ endpoint |
Event Driven¶
Outbound¶
Architectural concept¶
- InvestSuite posts events on one single topic of the client's event queue
- Any AMQP/MQTT compatible message bus is supported, eg. Kafka, RabbitMQ, Azure Service Bus...
Principles¶
-
The strategy is to signal that an event has happened, not to include all information that the middleware may possibly require (as these requirements may vary wildly and may introduce breaking changes). Therefore, an additional REST API call may be required to get additional data.
-
All events will use this envelope, with the actual information in
data
. -
The fully qualified name is a combination of the
subject
and theaction
mentioned in the event.
{
"id": string,
"created": timestamp,
"version": string,
"subject": string,
"action": string,
"data": dict
}
Field | Type | Content |
---|---|---|
id | string | An ID uniquely identifying the event |
created | timestamp | The timestamp of the creation of the event |
version | string | The version of the event object. For now, this will always be "1.0.0" |
subject | string | A string representing a subject, always referring to an entity (see Entities Overview) |
action | string | The action on the topic entity that triggers the event, for example 'creation' |
data | dict | A dictionary that contains specific fields, depending on the type, see below for the exhaustive list. |
Note
We are deliberately using ‘subject’ i.o. ‘topic’, since
-
the integration pattern with 3rd parties is that we will post all messages on 1 topic
-
the
subject
field can, potentially in the future, be broader than only entities (see above)
Portfolios¶
Creation¶
Info
Applies to: Robo Advisor, Self Investor
Fully qualified name: portfolios.creation
When IVS supplies the the front-end application to the b2b client they should be notified by IVS when a (real money) portfolio is created. That way, the client can store the IVS portfolio_id at their side and (optionally) link it to the user in their database.
It is up to the b2b client to check if the portfolio is “paper_money” or “real_money” based on this event. This to decide if they need to proceed with setup at their side.
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "portfolios",
"action" : "creation",
"data" : {
"id" : "a-portfolio-id",
"external_id" : "an-external-id",
"owned_by_user_id" : "a-user-id",
"owned_by_external_user_id": "portfolio-owner-external-user-id"
}
Status update¶
Info
Applies to: Robo Advisor, Self Investor
Fully qualified name: portfolios.status-update
Every time a status of a portfolio gets updated an event will be sent to the b2b client.
Possible values for the status field:
- WAITING_FOR_ACCOUNT: brokerage accounts are to be added by the b2b client
- WAITING_FOR_POLICY: user is still going through the risk profiling, so a policy was not yet determined.
- WAITING_FOR_FUNDS: waiting for incoming funds to arrive. This can be a trigger for the b2b client to push the client to make a transfer if this status isn’t updated within a certain timeframe.
- ACTIVE: portfolio is in use
- INACTIVE
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "portfolios",
"action" : "status-update",
"data" : {
"id" : "a-portfolio-id",
"external_id" : "an-external-id",
"value" : "INACTIVE",
"owned_by_user_id" : "a-user-id",
"owned_by_external_user_id": "portfolio-owner-external-user-id"
}
}
Possible values for the value field:
- WAITING_FOR_POLICY
- WAITING_FOR_ACCOUNT
- WAITING_FOR_FUNDS
- ACTIVE
- INACTIVE
Withdrawal request¶
Info
Applies to: Robo Advisor
Fully qualified name: portfolios.withdrawal
Whenever a user wants to withdraw money, IVS will update the divest amount on the portfolio object.
Every update to this amount will trigger an event towards the b2b client.
Divest amount:
- 1e100 → full withdrawal.
- a specific amount → partial withdrawal
Note: value is the total amount that is requested for withdrawal, not the incremental value (eg. in case of two quickly subsequent withdrawal requests, where the first is not yet settled)
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "portfolios",
"action" : "divest-amount-update",
"data" : {
"id" : "a-portfolio-id",
"external_id" : "an-external-id",
"divest_amount" : 1.234,
"currency" : "a-currency-code",
"owned_by_user_id" : "a-user-id",
"owned_by_external_user_id": "portfolio-owner-external-user-id"
}
}
Cash available for withdrawal¶
Info
Applies to: Robo Advisor and only if there is no broker integration
Fully qualified name: portfolios.cash-available-for-withdrawal
Whenever a withdrawal is requested (divest_amount
is set) or the holdings of a real_money
portfolio are updated, we check if there is sufficient cash available to execute the requested withdrawal. If this is the case, an event is sent to notify you that a withdrawal is possible, as InvestSuite will not execute any payment transactions towards the benefiary account.
The actual divest_amount
can be fetched by calling the portfolio endpoint.
Divest amount:
- 1e100 → full withdrawal.
- a specific amount → partial withdrawal
Note: value is the total amount that is requested for withdrawal, not the incremental value (eg. in case of two quickly subsequent withdrawal requests, where the first is not yet settled)
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "portfolios",
"action" : "cash-available-for-withdrawal",
"data" : {
"id" : "a-portfolio-id",
"external_id" : "an-external-id",
"currency" : "a-currency-code",
"owned_by_user_id" : "a-user-id",
"owned_by_external_user_id": "portfolio-owner-external-user-id"
}
}
Suitability profile created for a portfolio¶
Info
Only when Suitability profiler is used
Fully qualified name: portfolios.profile-created-for-portfolio
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "portfolios",
"action" : "profile-created-for-portfolio",
"data" : {
"id" : "a-portfolio-id",
"external_id" : "an-external-id",
"owned_by_user_id" : "a-user-id",
"suitability_profile_id": "profile-id-linked-to-portfolio",
"owned_by_user_id": "a-user-id",
"owned_by_external_user_id": "external-user-id"
}
}
Suitability profile updated for a portfolio¶
Info
Only when Suitability profiler is used
Fully qualified name: portfolios.profile-updated-for-portfolio
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "portfolios",
"action" : "profile-updated-for-portfolio",
"data" : {
"id" : "a-portfolio-id",
"external_id" : "an-external-id",
"owned_by_user_id" : "a-user-id",
"suitability_profile_id" : "profile-id-linked-to-portfolio",
"owned_by_user_id" : "a-user-id",
"owned_by_external_user_id" : "external-user-id"
}
}
Optimizations¶
Status update¶
Info
Applies to: Robo Advisor
Fully qualified name: optimizations.status-update
When an optimization is ready we will notify the b2b client when the optimization was successfully created.
Fields:
-
is_recommended: only when this is “True” the optimization will be executed (discretionary) or will be proposed to the client for acceptance (advisory).
-
Possible values for the status field: PENDING, SUCCESS, FAILURE, INFEASIBLE, NOT_OPTIMIZED
Info
In v1.0.0 (aug 2022) only status “SUCCESS” is used.
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "optimizations",
"action" : "status-update",
"data" : {
"id" : "an-optimization-id",
"portfolio_id" : "a-portfolio-id",
"external_id" : "an-external-id",
"is_recommended" : true,
"value" : "SUCCESS"
}
Owner choice update¶
Fully qualified name: optimizations.owner-choice-update
This event is specifically useful for b2b clients who implemented an advisory flow (advisory mandate).
In this case an optimization may only be executed when the owner choice has been updated to “ACCEPT”
The id can be used to fetch the optimization.
- Possible values for the status field are
ACCEPT
,REJECT
,REOPTIMIZE
,IGNORED
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "optimizations",
"action" : "owner-choice-update",
"data" : {
"id" : "an-optimization-id",
"portfolio_id" : "a-portfolio-id",
"external_id" : "an-external-id",
"value" : "ACCEPT"
}
}
Profiles¶
Assessment status update¶
Fully qualified name: profiles.assessment-status-update
Events are sent when a status of an assessment within a profile change. Assessments can return different states:
- IN_PROGRESS: the assessment is incomplete, either just created or started.
- COMPLETED: the assessment is completed but the result has not been accepted.
- ACCEPTED: final state, the assessment has been completed and the result has been accepted by the user.
COOLDOWN: An assessment has been tried multiple times and is in a state where a user has to wait until they can retry the assessment.- EXPIRED: Whenever an assessment has been ACCEPTED it can expire after a given period. This means the user will need to reassess.
To get the linked portfolio the query language can be used to fetch the portfolio which is linked to the mentioned by performing following call:
GET /portfolios/?query=config.manager_settings.suitability_profile_id+eq+'profile_id'
The state of a profile can be fetched based on the given 'profile_id':
GET /suitability-profiler/profiles/{profile_id}/
For more information see our Integration openAPI documentation.
{
"id" : "an-event-id",
"created" : "-1000000000-01-01T00:00:00Z",
"version" : "1.0.0",
"subject" : "assessments",
"action" : "assessment-status-update",
"data": {
"profile_id": "N1234...",
"questionnaire_id": {internal id}
"value": "IN_PROGRESS/COMPLETED/ACCEPTED/EXPIRED"
"accepted_datetime": timestamp (included only when status is ACCEPTED)}
}
REST /events/
endpoint¶
Inbound¶
Funding (deposit) event¶
POST /events/deposit/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Idempotency-Key: "LVRYWG833Vp2FIIG"
{
"data": {
"amount": "1000",
"currency": "USD",
"portfolio": "P01F8ZSNV0J45R9DFZ3D7D8C26F",
"external_id": "example_external_id"
}
}
Warning
In case of broker integration by InvestSuite, this event triggers the Funding flow.
Therefore we strongly suggest using the idempotency-key
to avoid double deposits if the middleware replays the event.
We recommend using the id of the transaction generated by the core banking system.
In case the broker is Saxo please limit the idempotency-key
to max 10 characters.
Withdrawal executed event¶
Info
Not to be confused with the withdrawal requested event.
POST /events/withdraw/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Idempotency-Key: "LVRYWG833Vp2FIIG"
{
"data": {
"amount": "1000",
"currency": "USD",
"portfolio": "P01F8ZSNV0J45R9DFZ3D7D8C26F"
}
}
Warning
We strongly suggest using the idempotency-key
to avoid double deposits.
Outbound¶
Warning
This is still under development