github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/docs/konnectors-workflow.md (about)

     1  [Table of contents](README.md#table-of-contents)
     2  
     3  # Konnectors workflow
     4  
     5  ## TL;DR
     6  
     7  ![Konnectors workflow](diagrams/konnector-workflow.png)
     8  
     9  ![Mermaid source file for diagram above](./diagrams/konnector-workflow.mmdc)
    10  
    11  ![Konnectors models](diagrams/konnector-models.png)
    12  
    13  ## Types
    14  
    15  ### Accounts
    16  
    17  `io.cozy.accounts` contains information for an account, including those for
    18  authentication (technically, they are encrypted by cozy-stack before being
    19  saved in CouchDB, but we will show them as the konnectors will see them):
    20  
    21  ```json
    22  {
    23      "auth": {
    24          "login": "000000000",
    25          "password": "**********"
    26      },
    27      "folderPath": "/Administratif/Free Mobile",
    28      "label": "freemobile",
    29      "namePath": "Free Mobile"
    30  }
    31  ```
    32  
    33  Accounts are manipulated through the `/data/` API.
    34  
    35  #### Decryption
    36  
    37  The decryption of the credentials is reserved to requests coming from konnectors
    38  and services. For services, it is only available on
    39  `/data/io.cozy.acconts/:account-id` with an additional `include=credentials`
    40  parameter.
    41  
    42  #### Aggregator accounts
    43  
    44  Some konnectors are based on an aggregator service. An aggregator is declared
    45  in the konnector manifest and specify an `accountId` property. When Cozy-Home
    46  detects this property, it checks if an account with this given id exists. If not,
    47  it creates it. Every account created for a konnector based on an aggregator is
    48  then related to this aggregator account. This relationship is called `parent`
    49  account.
    50  
    51  ```json
    52  {
    53      "auth": {
    54          "login": "000000000",
    55          "password": "**********"
    56      },
    57      "folderPath": "/Administratif/Bankbank",
    58      "label": "Bankbank",
    59      "namePath": "Bankbank",
    60      "relationships": {
    61        "parent": {
    62          "data": {
    63            "_id": "service-aggregator-account",
    64            "_type": "io.cozy.accounts"
    65          }
    66        }
    67      }
    68  }
    69  ```
    70  
    71  **Note:** you can read more about the [accounts doctype
    72  here](https://docs.cozy.io/en/cozy-doctypes/docs/io.cozy.accounts/).
    73  
    74  ### Konnectors
    75  
    76  `io.cozy.konnectors` are installed similarly to `io.cozy.apps` (see
    77  [doc](./konnectors.md))
    78  
    79  ### Permissions
    80  
    81  Like client-side applications, each konnector has an associated
    82  `io.cozy.permissions`. These permissions are those listed on the manifest of
    83  the konnectors, and the stack will add a permission for the files on the folder
    84  choosen by the user.
    85  
    86  ### Triggers
    87  
    88  `io.cozy.triggers` are used to define when konnectors are launched. See
    89  https://docs.cozy.io/en/cozy-stack/jobs/#post-jobstriggers
    90  
    91  
    92  ## Complete flow example
    93  
    94  As a user, from the expenses management app, I have a clean flow to configure a
    95  connector to retrieve my travel expenses
    96  
    97  1 - User is in **my-expenses** and clicks on \[configure travels\]
    98  
    99  2 - **my-expenses** triggers an intent
   100  
   101  ```javascript
   102  cozy.intents.start("CREATE", "io.cozy.konnectors", {
   103      category: "transport"
   104  });
   105  ```
   106  
   107  3 - the Store app catch the intent, fetch all available konnectors and let the
   108  user choose
   109  
   110  ```http
   111  GET /registry?... HTTP/1.1
   112  ```
   113  
   114  4 - the user chooses the trainline konnector. Its manifest looks like this:
   115  
   116  ```json
   117  {
   118      "name": "Trainline",
   119      "type": "konnector",
   120      "slug": "konnector-trainline",
   121      "description": "Konnector for trainline . com",
   122      "source": "https://github.com/konnectors/trainlines.git@build",
   123      "developer": {
   124          "name": "XXX",
   125          "url": "https://www.xxx.fr"
   126      },
   127      "version": "3.0.0",
   128      "licence": "AGPL-3.0",
   129      "fields": {
   130          "login": {
   131              "type": "text"
   132          },
   133          "password": {
   134              "type": "password"
   135          },
   136          "advancedFields": {
   137              "folderPath": {
   138                  "advanced": true,
   139                  "isRequired": false
   140              }
   141          }
   142      },
   143      "category": "transport",
   144      "frequency": "weekly",
   145      "permissions": {
   146          "events": {
   147              "description": "Connect train bill with  event in your calendar",
   148              "type": "io.cozy.events",
   149              "verbs": ["PATCH"]
   150          }
   151      }
   152  }
   153  ```
   154  
   155  5 - the user clicks on install, and the install starts:
   156  
   157  ```http
   158  POST /konnectors/trainline HTTP/1.1
   159  ```
   160  
   161  6 - the Store then uses an intent to know which app can configure this konnector:
   162  
   163  ```javascript
   164  cozy.intents.start("REDIRECT", "io.cozy.accounts", {
   165      slug: "trainline"
   166  });
   167  ```
   168  
   169  7 - the Store redirects to the Home. The Home asks the user for account config
   170  and create a folder to save the PDFs.
   171  
   172  8 - the Home also add a Reference from the konnector to the folder to prevent
   173  any accidental folder deletion:
   174  
   175  ```http
   176  POST /files/123-selected-folder-id-123/relationships/referenced_by
   177  ```
   178  
   179  ```json
   180  {
   181      "data": [
   182          {
   183              "type": "io.cozy.konnectors",
   184              "id": "io.cozy.konnectors/trainlines"
   185          }
   186      ]
   187  }
   188  ```
   189  
   190  9 - then the Home can create the account:
   191  
   192  ```http
   193  POST /data/io.cozy.accounts HTTP/1.1
   194  ```
   195  
   196  ```json
   197  {
   198      "account_type": "trainline",
   199      "auth": {
   200          "login": "xxxx",
   201          "password": "yyyyy"
   202      },
   203      "folderPath": "/Administrative/Trainline"
   204  }
   205  ```
   206  
   207  ```http
   208  HTTP/1.1 200 OK
   209  ```
   210  
   211  ```json
   212  {
   213      "_id": "123-account-id-123",
   214      "_rev": "1-asasasasa",
   215      "account_type": "trainline",
   216      "auth": {
   217          "login": "xxxx"
   218      },
   219      "folderPath": "/Administrative/Trainline"
   220  }
   221  ```
   222  
   223  10 - the Home create the trigger:
   224  
   225  ```http
   226  POST /jobs/io.cozy.triggers
   227  ```
   228  
   229  ```json
   230  {
   231      "data": {
   232          "attributes": {
   233              "type": "@cron",
   234              "arguments": "0 0 0 0 1 1 ",
   235              "worker": "konnector",
   236              "message": {
   237                  "konnector": "trainline",
   238                  "account": "5165621628784562148955",
   239                  "folder_to_save": "877854878455"
   240              }
   241          }
   242      }
   243  }
   244  ```
   245  
   246  11 - and, finally, the Home runs the konnector for the first time:
   247  
   248  ```http
   249  POST /jobs/triggers/abc159753/launch HTTP/1.1
   250  ```
   251  
   252  12 - the Home uses the [realtime](./realtime.md) to be informed if the
   253  konnector fails to login or succeeds to import the PDFs.
   254  
   255  If the user wants to use several account, the Home can setup several triggers
   256  for the same konnector & various accounts.
   257  
   258  
   259  ## Konnector Worker specs
   260  
   261  ### Prepare the execution
   262  
   263  The cozy-stack prepares the execution of the konnector by doing these steps:
   264  
   265  - it checks that the konnector is not in maintenance in the registry (except
   266    for manual execution)
   267  - it ensures that the konnector has a folder where it can write its files,
   268    and has the permission to write in this folder.
   269  
   270  ### Execute the konnector
   271  
   272  Start the konnector through nsjail, passing as ENV variables:
   273  
   274      - `COZY_URL`:          the starting instance URL
   275      - `COZY_CREDENTIALS`:  security token to communicate with Cozy
   276      - `COZY_FIELDS`:       JSON-encoded message with the arguments from the trigger
   277      - `COZY_PAYLOAD`:      JSON-encoded payload from the HTTP request (@webhook trigger)
   278      - `COZY_PARAMETERS`:   JSON-encoded parameters from the konnector manifest
   279      - `COZY_LANGUAGE`:     the language field of the konnector (eg. "node" etc.)
   280      - `COZY_LOCALE`:       the locale of the user (eg. "en" etc.)
   281      - `COZY_TIME_LIMIT`:   how much time the konnector can run before being killed
   282      - `COZY_JOB_ID`:       id of the job
   283      - `COZY_TRIGGER_ID`:   id of the trigger that has created the job
   284      - `COZY_JOB_MANUAL_EXECUTION`: whether the job was started manually (in Home) or automatically (via a cron trigger or event)
   285  
   286  The konnector process can send events trough its stdout (newline separated JSON
   287  object), the konnector worker pass these events to the realtime hub as
   288  `io.cozy.jobs.events`.
   289  
   290  -   Only JSON formatted events are forwarded to the client-side through
   291      realtime
   292  -   Otherwise formatted lines (such as node Error) will be kept in some system
   293      logs.
   294  
   295  Konnectors should NOT log the received account login values in production.
   296  
   297  ### Konnector error handling
   298  
   299  The konnector can output json formated messages as stated before (the events)
   300  and those events will be typed and formatted like this:
   301  
   302  ```javascript
   303  {
   304      type: "messagetype",  // can be "debug", "info", "warning", "error", and "critical"
   305      message: "message"    // can be any string
   306  }
   307  ```
   308  
   309  If there is an error or critical message, the execution will be seen as a
   310  failure by cozy-stack. It's also the case if the konnector reaches the timeout
   311  or returns with a non-zero status code.
   312  
   313  **Note:** debug and info level are not transmitted to syslog, except if the
   314  instance is in debug mode. It would be too verbose to do otherwise.
   315  
   316  The message can start with a known keyword that could have special meanings
   317  for `cozy-stack` or for statistical analysis of konnector health.
   318  Known keywords are listed in the [konnector tutorial](https://docs.cozy.io/en/tutorials/konnector/going-further/#error-handling)
   319  
   320  When a connector throw an error of either `LOGIN_FAILED` or any error starting
   321  with `USER_ACTION_NEEDED` (with the exception of
   322  `USER_ACTION_NEEDED.CGU_FORM`), meaning that the user needs to execute an
   323  action on provider's website (either changing its credentials or
   324  acknowledging something on provider website), then `cozy-stack` will skip all
   325  subsequent automatic executions to avoid querying the provider site and
   326  blocking user account. Manual execution by the user will still be possible to
   327  unlock the konnector/account when the user thinks it is now ready to be run
   328  again.
   329  
   330  ### Account deleted
   331  
   332  When an account is deleted, or a konnector is going to be uninstalled, the
   333  konnector is executed with the `account_deleted` field to true, so it can clean
   334  the account remotely.
   335  
   336  
   337  ## OAuth (and service secrets)
   338  
   339  ### Doctypes
   340  
   341  `io.cozy.konnectors` gives their desiderata for an account
   342  
   343  ```json
   344  {
   345      "fields": {
   346          "account": {
   347              "doctype": "io.cozy.accounts",
   348              "account_type": "maif",
   349              "scope": "openid profile offline_access"
   350          }
   351      }
   352  }
   353  ```
   354  
   355  `io.cozy.accounts` contains authentication information for an account, as well
   356  as the associated scope
   357  
   358  ```json
   359  {
   360      "name": "Mon Compte Maif",
   361      "account_type": "maif",
   362      "status": "connected",
   363      "oauth": {
   364          "access_token": "akosaksoakso",
   365          "refresh_token": "okoakozkaozk",
   366          "scope": "openid profile offline_access"
   367      }
   368  }
   369  ```
   370  
   371  `io.cozy.account_types` contains the oauth configuration:
   372  
   373  ```json
   374  {
   375      "_id": "service.example",
   376      "grant_mode": "authorization_code",
   377      "client_id": "the registered client id",
   378      "client_secret": "client_secret is necessary for server-flow",
   379      "auth_endpoint": "https://service.example/auth",
   380      "token_endpoint": "https://api.service.example/token"
   381  }
   382  ```
   383  
   384  `io.cozy.account_types` are not accessible to the applications: they are
   385  injected directly in CouchDB in a global database
   386  `secrets/io-cozy-account_types`.
   387  
   388  ### Secrets that are not OAuth
   389  
   390  The `io.cozy.account_types` doctype can also be used for storing secrets that
   391  are not related to OAuth. The document still need to be injected manually in
   392  `secrets/io-cozy-account_types`:
   393  
   394  ```
   395  {
   396      "_id": "service.example",
   397      "grant_mode": "secret",
   398      "slug": "service",
   399      "secret": "th1$_1$_th3_s3cr3t!"
   400  }
   401  ```
   402  
   403  **Note**: `grant_mode` must be `secret` (or `bi_webauth+secret`, or
   404  `bi_webview+secret`, or `authorization_code+secret`), `slug` must be the slug
   405  of the konnector,  but `secret` can be a map instead of a simple string if
   406  several secrets are needed for this service.
   407  
   408  The secret is given to the konnector in the `COZY_PARAMETERS` env variable.
   409  
   410  ### Overloading an account type for a given context
   411  
   412  It is possible to use a different account type for a given context, by creating
   413  a new document with an id prefix by the context name and `/`. For example, a
   414  different secret can be used in the `foobar` context by injecting this document
   415  in `secrets/io-cozy-account_types`:
   416  
   417  ```
   418  {
   419      "_id": "foobar/service.example",
   420      "grant_mode": "secret",
   421      "slug": "service",
   422      "secret": "th1$_1$_n0t_th3_s4m3_s3cr3t!"
   423  }
   424  ```
   425  
   426  ### Reminder OAuth flow
   427  
   428  **Service** is the website from which konnector aims to retrieve informations.
   429  **Stack** is the cozy stack.
   430  
   431  OAuth is divided in 3 steps:
   432  
   433  -   Client Registration: the client application (the Stack) needs to be
   434      registered with the Service
   435  -   Obtaining & Refreshing Authorization: all the steps from `client_id` to
   436      `access_token`, the Stack will handle those
   437  -   Using the `access_token`: ideally, the konnector should only concern itself
   438      with this part; it receives an `access_token` and uses it.
   439  
   440  #### Client Registration
   441  
   442  Before beginning the Grant process, most Services require the application to be
   443  registered with a defined `redirect_uri`.
   444  
   445  There are a lot of options, which we will choose from when actually
   446  implementing konnectors using them.
   447  
   448  ### Manually
   449  
   450  Most services requires a human developer to create the client manually and
   451  define its redirect_uri. However each instance has its own domain, so for these
   452  services, we will need to:
   453  
   454  **A. Register a "proxy" client**, which is a static page performing redirections
   455  as needed, as was done for Facebook events in v2 konnectors. We will register a
   456  well known cozy domain, like `oauth-proxy.cozy.io` and registers it with all
   457  providers. The use and risks associated with this domain should be made clear to
   458  the user.
   459  
   460  **B. Register each Stack** with a redirect_uri on the main stack domain, if we
   461  go this way, the register_uri below moves from
   462  `bob.cozy.rocks/accounts/service/redirect` to
   463  `oauthcallback.cozy.rocks/accounts/service/redirect` and the domain will be
   464  prepended to the state. This is feasible at cozy scale, but requires more
   465  knowledge and work for self-hosters.
   466  
   467  #### Example: Google
   468  
   469  For example, these are the steps for creating the google `account_type` for the
   470  stack at .mycozy.cloud:
   471  
   472  -   Go to https://console.developers.google.com
   473  -   Select or Create Project (up left near the logo)
   474  -   Enable desired APIs (TBD with usages)
   475  -   Click "Credentials"
   476  -   Create credentials > Oauth Client ID > Web application
   477  -   Set redirectURI to
   478      https://oauthcallback.mycozy.cloud/accounts/google/redirect
   479  -   Copy and paste provided Client ID and Client Secret.
   480  
   481  Then save the data in the console
   482  
   483  ```
   484  http PUT localhost:5984/secrets%2Fio-cozy-account_types/google
   485  grant_mode=authorization_code
   486  redirect_uri="https://oauthcallback.mycozy.cloud/accounts/google/redirect"
   487  token_mode=basic
   488  token_endpoint="https://www.googleapis.com/oauth2/v4/token"
   489  auth_endpoint="https://accounts.google.com/o/oauth2/v2/auth"
   490  client_id=$CLIENT_ID
   491  client_secret=$CLIENT_SECRET
   492  ```
   493  
   494  #### Dynamic Registration Protocol
   495  
   496  A few services allows client to register programatically through the Dynamic
   497  Client Registration Protocol [RFC7591](https://tools.ietf.org/html/rfc7591), we
   498  should allow the Stack to register using this protocol when we first need to
   499  implement a Konnector connecting to such a Service.
   500  
   501  #### No redirect_url enforcement
   502  
   503  A few services allows to specify arbitrary redirect_url without registering
   504  beforehand as a client.
   505  
   506  ### Authorization Grant flows
   507  
   508  #### webserver flow (Authorization Code Grant)
   509  
   510  A. In the cozy app (Home) give a link
   511  
   512  ```html
   513  <a href="https://bob.cozy.rocks/accounts/service-name/start?
   514    scope=photos&
   515    state=1234zyx">
   516  ```
   517  
   518  **Notes:**
   519  
   520  - the scope may depends on other fields being configured (checkboxes),
   521  this will be described in json in the konnectors manifest. The format will be
   522  determined upon implementation;
   523  - to limit bandwidth and risk of state corruption, the cozy app should
   524  save its state under a random key into localStorage, the key is then passed as
   525  the state in this query;
   526  - a third parameter, `slug`, can be added to redirect to this app instead of
   527    the home.
   528  
   529  B. Service lets the user login, allows or denies the scope, then redirects to
   530  
   531  ```http
   532  https://oauthcallback.cozy.rocks/accounts/service-name/redirect?
   533    code=AUTH_CODE_HERE&
   534    state=1234zyx
   535  ```
   536  
   537  C. The Stack does Server side this request:
   538  
   539  ```http
   540  POST /oauth/access_token HTTP/1.1
   541  Host: api.service.example
   542  Content-Type: application/x-www-form-urlencoded
   543  
   544  grant_type=authorization_code&
   545    code=AUTH_CODE_HERE&
   546    redirect_uri=https://oauthcallback.cozy.rocks/accounts/service-name/redirect&
   547    client_id=CLIENT_ID&
   548    client_secret=CLIENT_SECRET
   549  ```
   550  
   551  Note: the stack will also send the `state` parameter on this request. It is not
   552  mandatory per the OAuth 2.0 spec, and the `skip_state_on_token` option can be
   553  used to cancel this behavior if the provider throw an error if this parameter
   554  is present.
   555  
   556  D. The Service responds (server side) with:
   557  
   558  ```json
   559  {
   560      "access_token": "ACCESS_TOKEN",
   561      "token_type": "bearer",
   562      "expires_in": 2592000,
   563      "refresh_token": "REFRESH_TOKEN",
   564      "scope": "read",
   565      "uid": 100101,
   566      "info": {
   567          "name": "Claude Douillet",
   568          "email": "claude.douillet@example.com"
   569      }
   570  }
   571  ```
   572  
   573  This whole object is saved as-is into a `io.cozy.accounts` 's `extras` field.
   574  
   575  The known fields `access_token`, `refresh_token` & `scope` will be **also**
   576  saved on the account's `oauth` itself
   577  
   578  E. The Stack redirect the user to the cozy app:
   579  
   580  ```http
   581  HTTP/1.1 302 Found
   582  Location: https://bob-home.cozy.rocks/?state=1234zyx&account=accountID
   583  ```
   584  
   585  The Cozy app checks that the state is expected, and restores its state to the
   586  form but whith account completed.
   587  
   588  #### SPA flow (Implicit grant)
   589  
   590  A. The cozy app gives a link:
   591  
   592  ```html
   593  <a href="https://service.example/auth? (url)
   594      response_type=token&
   595      client_id=CLIENT_ID&
   596      redirect_uri=https://bob-home.cozy.rocks/&
   597      scope=photos&
   598      state=1234zyx">
   599  ```
   600  
   601  See server-flow for state rules.
   602  
   603  B. Service lets the user login, allows or denies the scope, then redirects to
   604  
   605  ```http
   606  https://bob-home.cozy.rocks/?access_token=ACCESS_TOKEN&state=1234zyx
   607  ```
   608  
   609  C. The Cozy app adds the token to the `io.cozy.accounts` and save it before
   610  starting konnector.
   611  
   612  ### Accessing data
   613  
   614  Once we have an account, the Cozy app starts the konnector with the proper
   615  account id. The konnector can then fetch the account and use its `access_token`
   616  to performs a request to fetch data:
   617  
   618  ```http
   619  POST /resource HTTP/1.1
   620  Host: api.service.example
   621  Authorization: Bearer ACCESS_TOKEN
   622  ```
   623  
   624  ### Refreshing token
   625  
   626  When using the server-flow, we also get a refresh_token. It is used to get a new
   627  access_token when it expires. However, if konnectors are responsible for
   628  refreshing token there is a race condition risk:
   629  
   630  ```
   631  (konnector A) GET  https://api.service.com/resource TOKEN1     -> expired
   632  (konnector B) GET  https://api.service.com/resource TOKEN1     -> expired
   633  (konnector A) POST https://api.service.com/token REFRESH_TOKEN -> TOKEN2a
   634  (konnector B) POST https://api.service.com/token REFRESH_TOKEN -> TOKEN2b
   635  (konnector A) GET  https://api.service.com/token TOKEN2a       -> invalid
   636  ```
   637  
   638  To avoid this, the stack will be responsible to perform token refresh. A
   639  konnector can requires the stack to refresh an account token with an HTTP
   640  request.
   641  
   642  ```http
   643  POST /accounts/:accountType/:accountID/refresh HTTP/1.1
   644  Host: bob.cozy.rocks
   645  ```
   646  
   647  ### Konnectors Marketplace Requirements
   648  
   649  The following is a few points to be careful for in konnectors when we start
   650  allowing non-cozy developped OAuth konnectors.
   651  
   652  -   With SPA flow, because of advanced security concerns (confused deputy
   653      problem), cozy should validate the `access_token`. However, the way to do
   654      that depends on the provider and cannot be described in json, it is
   655      therefore the responsibility of the konnector itself.
   656  
   657  ### Account types security rules
   658  
   659  -   With server flow, an evil account type with proper `auth_endpoint` but bad
   660      `token_endpoint` could retrieve a valid token as well as cozy client secret.
   661      The reviewer of an `account_type` should make sure both these endpoints are
   662      on domains belonging to the Service provider.
   663  
   664  ### Notes for MesInfos experiment
   665  
   666  -   MAIF konnector uses the webserver flow without redirect_uri validation
   667  -   Orange konnector uses the client-side proxy but hosted on their own servers
   668      (/!\ redirect_uri vs redirect_url)
   669  
   670  ### Webauth
   671  
   672  For some banks integration (Paypal, Orange Bank, Revolut…), Budget Insight has
   673  a workflow similar to OAuth called
   674  [Webauth](https://docs.budget-insight.com/reference/connections#webauth).
   675  
   676  It is possible to use this workflow for konnectors by registering an account
   677  type with the following parameter:
   678  
   679  - `grant_mode`, with `bi_webauth` as the value (or `bi_webauth+secret` if there is also a secret)
   680  - `redirect_uri`, with an URL like `https://oauthcallback.mycozy.cloud/accounts/paypal/redirect`
   681  - `client_id`, with the client ID given by Budget Insight
   682  - `auth_endpoint`, with `https://{domain}.biapi.pro/2.0/webauth` (with the correct `domain`)
   683  - `reconnect_endpoint`, with `https://{domain}.biapi.pro/2.0/auth/webview/reconnect` (idem).
   684  
   685  ### BI Weview
   686  
   687  A new integration of Budget Insight is available for all the proposed banks.
   688  This is a workflow similar to OAuth called
   689  [webview](https://docs.budget-insight.com/reference/webview)
   690  
   691  It is possible to use this workflow for konnectors by registering an account
   692  type with the following parameter:
   693  
   694  - `grant_mode`, with `bi_webview+secret` as the value
   695  - `redirect_uri`, with an URL like `https://oauthcallback.mycozy.cloud/accounts/paypal/redirect`
   696  - `client_id`, with the client ID given by Budget Insight
   697  - `auth_endpoint`, with `https://{domain}.biapi.pro/2.0/auth/webview/fr/connect` (with the correct `domain`)
   698  - `manage_endpoint`, with `https://{domain}.biapi.pro/2.0/auth/webview/reconnect` (idem).
   699  - `reconnect_endpoint`, with `https://{domain}.biapi.pro/2.0/auth/webview/reconnect` (idem).
   700  
   701  #### Manage
   702  
   703  The [manage webview](https://docs.budget-insight.com/reference/webview#manage-connections)
   704  can be called with this route:
   705  
   706  ```http
   707  GET /accounts/:accountType/:accountID/manage?code={code}&connection_id={id}&slug={slug}&state={state} HTTP/1.1
   708  Host: jane.cozy.example
   709  ```
   710  
   711  ```http
   712  HTTP/1.1 303 See Other
   713  Location: https://domain.biapi.pro/2.0/auth/webview/manage
   714      ?client_id={client_id}
   715      &code={code}
   716      &connection_id={id}
   717      &redirect_uri=https://oauthcallback.cozy.example/accounts/{accountType}/redirect
   718      &state={stackState}
   719  ```
   720  
   721  At the end of flow, the user will be redirected to
   722  `https://jane-{slug}.cozy.example/?state={state}`.
   723  
   724  #### Reconnect
   725  
   726  The [reconnection webview](https://docs.budget-insight.com/guides/handle-scas-connection-states#use-our-reconnection-webview)
   727  can be called with this route:
   728  
   729  ```http
   730  GET /accounts/:accountType/:accountID/reconnect?code={code}&connection_id={id}&slug={slug}&state={state} HTTP/1.1
   731  Host: jane.cozy.example
   732  ```
   733  
   734  ```http
   735  HTTP/1.1 303 See Other
   736  Location: https://domain.biapi.pro/2.0/auth/webview/reconnect
   737      ?client_id={client_id}
   738      &code={code}
   739      &connection_id={id}
   740      &redirect_uri=https://oauthcallback.cozy.example/accounts/{accountType}/redirect
   741      &state={stackState}
   742  ```
   743  
   744  At the end of flow, the user will be redirected to
   745  `https://jane-{slug}.cozy.example/?state={state}`.
   746  
   747  ## Webhook triggers
   748  
   749  ![Webhook trigger for a konnector](diagrams/konnector-webhook.jpeg)
   750  
   751  1. The cloudery (or an application) can install the konnector and create an account
   752  
   753  2. It can also create the webhook trigger via a `POST /jobs/triggers`
   754  
   755  Request:
   756  
   757  ```http
   758  POST /jobs/triggers HTTP/1.1
   759  Host: jane.cozy.example
   760  Content-Type: application/vnd.api+json
   761  ```
   762  
   763  ```json
   764  {
   765    "data": {
   766      "attributes": {
   767        "type": "@webhook",
   768        "worker": "konnector",
   769        "message": {
   770          "param_from_trigger": "foo"
   771        }
   772      }
   773    }
   774  }
   775  ```
   776  
   777  Response:
   778  
   779  ```http
   780  HTTP/1.1 200 OK
   781  Content-Type: application/vnd.api+json
   782  ```
   783  
   784  ```json
   785  {
   786    "data": {
   787      "type": "io.cozy.triggers",
   788      "id": "0915b6b0-0c97-0139-5af7-543d7eb8149c",
   789      "attributes": {
   790        "type": "@webhook",
   791        "worker": "konnector",
   792        "message": {
   793          "konnector": "mykonnector",
   794          "param_from_trigger": "foo"
   795        }
   796      },
   797      "links": {
   798        "self": "/jobs/triggers/0915b6b0-0c97-0139-5af7-543d7eb8149c",
   799        "webhook": "https://jane.cozy.example/jobs/webhooks/0915b6b0-0c97-0139-5af7-543d7eb8149c"
   800      }
   801    }
   802  }
   803  ```
   804  
   805  3. The cloudery can then give the webhook URL to the external service
   806  
   807  4. When the external service has new documents to import, it can call the webhook
   808  
   809  Request:
   810  
   811  ```http
   812  POST /jobs/webhooks/0915b6b0-0c97-0139-5af7-543d7eb8149c HTTP/1.1
   813  Host: jane.cozy.example
   814  Content-Type: application/json
   815  ```
   816  
   817  ```json
   818  {"param_from_http_body": "bar"}
   819  ```
   820  
   821  Response:
   822  
   823  ```http
   824  HTTP/1.1 204 No Content
   825  ```
   826  
   827  5. The stack will put a job in its queue for the konnector
   828  
   829  6. The stack executes the konnector with:
   830  
   831  - `COZY_FIELDS={"param_from_trigger": "foo"}`
   832  - `COZY_PAYLOAD={"param_from_http_body": "bar"}`
   833  - etc.
   834  
   835  **Note:** the environnement variables have a limit for their size defined by
   836  the linux kernel. If the payload is too big to fit inside the env variable,
   837  the stack will put the payload in a temporary file, and will set the
   838  `COZY_PAYLOAD` variable to `@` + the filename of this file, like:
   839  
   840  `COZY_PAYLOAD=@cozy_payload.json`
   841  
   842  7. The konnector will fetch the documents from the external service and save
   843     them in the Cozy.
   844  
   845  Note: if the trigger has a debounce, the `COZY_PAYLOAD` will be an object with
   846  a `payloads` array, like this:
   847  
   848  ```sh
   849  COZY_PAYLOAD={"payloads": [{"param_from_http_body": "bar"}]}
   850  ```