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 ```