{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"2885587f-fa06-4df0-96ff-a2ceb2733f31","name":"Business Logic Service API v1.4","description":"APIs for interacting with Business Logic Service Engine.\n\n<img src=\"https://content.pstmn.io/3a333d0a-9029-4e6b-9042-2cfbd684bc42/aW1hZ2UucG5n\" width=\"580\" height=\"298\">\n\n## Business Logic Service\n\nThe business logic service is ETI's Integration as a Service product.\n\n<img src=\"https://content.pstmn.io/695966fe-d9d3-42b4-8066-6b349132e169/aW1hZ2UucG5n\" alt=\"Business%20Logic%20Service\" width=\"552\" height=\"581\">\n\nThe engine is a cloud native service designed to perform integration-as-a-service tasks. Event streams may be subscribed for event processing.\n\n<img src=\"https://eti-software.imgix.net/Webhooks.png\" width=\"452\" height=\"313\">\n\n## General Purpose\n\n<img src=\"https://eti-software.imgix.net//BLS-LowCode-General.png\" alt=\"General%20Purpose\" width=\"446\" height=\"154\">\n\n## Northbound API\n\n| **API** | **Description** |\n| --- | --- |\n| **Generic Pipeline Processor** | Endpoint for processing a generic pipeline. |\n| **OBAPI Server** | OBAPI Server endpoints for managing pipelines on behalf of OBAPI Clients. OBAPI shares Triad events, described in JSON, to event processors. See [Triad's Outbound API (OBAPI)](https://obapi.etisoftware.com/) |\n| **Event Stream Subscriptions** | Event stream subscriptions can be established and have their events filtered and processed. |\n\n### OpenAPI\n\nThe OpenAPI Specification, formerly known as the Swagger Specification, is the world’s standard for defining RESTful interfaces. The OAS enables developers to design a technology-agnostic API interface that forms the basis of their API development and consumption.\n\n### Recursion and Overriding Name and Version\n\n_Recursion_ is the process a procedure goes through when one of the steps of the procedure involves invoking the procedure itself. Allowing overrideName and overrideVersion query parameters allows for simple conditional logic and recursion to be combined. This might be quite helpful if one wanted a Pipeline Configuration to be executed by something other than request body content.\n\n### Persistence\n\nTactically we chose MongoDB for storage for efficiency and productivity in storing hierarchical data. Describing the inner parts of a chassis made 4 years in the future is difficult, and describing the integration sequence and objects supported is even tougher. Offering a completely reconcilable solution that records the initial conditions and action taken with appropriate attribution presented this problem again. For this reason it was decided that the Business Logic Service Integration-as-a-Service suite will use MongoDB.\n\n| **Collection** | **Purpose** |\n| --- | --- |\n| **PipelineConfigs** | Pipeline configurations are the main data structures that drive BLS logic execution. This collection contains the data which is used to build stages when executing a pipeline. |\n| **CommandLibrary** | This collection contains commands from specifications of remote APIs. Commands from a library can be used directly in templates to gather data from remote systems or called as part of a pipeline. See the accompanying JINJA template examples. |\n| **PipelineData** | This collection contains dynamic settings used to simplify the data-mapping exercises traditionally performed by the integrator. |\n| **LastProvisioned** | This collection contains data about the last known successful provisioning attempt. When a pipeline is successfully finished, data from all stages is gathered together with basic request information and persisted into this collection for transparency and/or further processing. |\n| **NetworkQuery** | This collection contains resulting documents describing the state of network electronics at the time of the query. |\n\n## Pipeline Processor\n\nEngine is designed to receive requests of varying payloads.\n\n<img src=\"https://content.pstmn.io/c207c2db-a827-485d-98f5-d30f82b83370/aW1hZ2UucG5n\" alt=\"Business%20Logic%20Service\" width=\"534\" height=\"562\">\n\n### Pipelines\n\nThe Pipeline pattern uses ordered stages to process a sequence of input values. Each implemented task is represented by a stage of the pipeline. Pipelines are similar to assembly lines in a factory, where each item in the assembly line is constructed in stages. The partially-assembled item is passed from one assembly stage to another. The output of the assembly line occurs in the same order as that of the inputs.\n\n#### Pipeline Structure\n\n##### root\n\n| **Property** | **Purpose** | **Mandatory**  <br>**Optional** | **Type**  <br>**Default value** |\n| --- | --- | --- | --- |\n| **schema_version** | version of golden configuration syntax | **M** | **string** |\n| **nameVersion** | golden configuration identifier object | **M** | **object** |\n| enabled | Jinja expression to determine whether the golden configuration is enabled | **O** | **string**  <br>default: true |\n| logicProfile | logic profile which defines how the processing of this golden configuration is done. Currently there are two profiles available:  <br>1\\. **TARGET_CURRENT_DELTA**  <br>2\\. **SIMPLE** | **O** | **string**  <br>default: TARGET_CURRENT_DELTA |\n| **description** | brief purpose of this golden configuration | **M** | **string** |\n| **contentType** | content type of template payload data | **M** | **string** |\n| **template** | template payload and commands object | **M** | **object** |\n| persistState | save provisioning data into LastProvisioned | **O** | **boolean**  <br>default: true |\n| failOnError | if true, the whole pipeline fails in case of error | **O** | **boolean**  <br>default: false |\n\n##### nameVersion\n\n| **Property** | **Purpose** | **Mandatory**  <br>**Optional** | **Type**  <br>**Default value** |\n| --- | --- | --- | --- |\n| **name** | unique identifier of provisioned service using a specific technology | **M** | **string** |\n| **version** | version of this golden configuration | **M** | **string** |\n| **sequence** | provisioning sequence number determines in what order golden configurations will be executed during provisioning | **M** | **integer** |\n| delSequence | deprovisioning sequence number determines in what order golden configurations will be executed during deprovisioning.  <br>If not present, this golden configuration will not be executed as part of deprovisioning pipeline | **O** | **integer**  <br>default: none |\n\n##### template\n\n| **Property** | **Purpose** | **Mandatory**  <br>**Optional** | **Type**  <br>**Default value** |\n| --- | --- | --- | --- |\n| **data** | template payload data which can contain Jinja expressions. It is rendered using supplied placeholders which are resolved during runtime | **M** | **string** |\n| exclusions | when using TARGET_CURRENT_DELTA logic profile, these properties/objects are not considered during diffing | **O** | **array of PathSpec objects**  <br>default: none |\n| immutables | when using TARGET_CURRENT_DELTA logic these properties must be preserved. If CURRENT properties differ from TARGET, they must be replaced inline or recreated using DELETE/CREATE sequence | **O** | **array of PathSpec objects**  <br>default: none |\n| **commands** | commands to be executed as part of provisioning logic. There are 7 types of operations which can be executed: NOOP, CREATE, READ, UPDATE, DELETE, OPERATION and SESSION | **M** | **array of Command objects** |\n\n##### pathSpec\n\n| **Property** | **Purpose** | **Mandatory**  <br>**Optional** | **Type**  <br>**Default value** |\n| --- | --- | --- | --- |\n| **recreate** | Value indicating whether the immutable part should be replaced inline or recreate commands need to be sent | **O** | **boolean**  <br>  <br>**false** |\n| **path** | JSON pointer as specified by [RFC6901](https://tools.ietf.org/html/rfc6901) pointing to the property or object | **M** | **string** |\n| **description** | description of the path specification | **M** | **string** |\n\n##### commands\n\n| **Property** | **Purpose** | **Mandatory**  <br>**Optional** | **Type**  <br>**Default value** |\n| --- | --- | --- | --- |\n| **type** | Command type, in case of HTTP command the value must be **HTTP** | **M** | **string: HTTP** |\n| condition | Jinja expression to determine whether this command should be executed | **O** | **string**  <br>default: true |\n| **operation** | Type of operation, one of: NOOP, CREATE, READ, UPDATE, DELETE, OPERATION and SESSION | **M** | **string** |\n| action | Type of action, currently used only in SESSION operation. One of: LOGIN and LOGOUT | **O** | **string**  <br>default: none |\n| **description** | description of operation this command is supposed to do | **M** | **string** |\n| **method** | HTTP method to use | **M** | **string** |\n| **url** | URL of remote API to connect to | **M** | **string** |\n| auth | authentication object representing authentication method used with the request | **O** | **authentication object**  <br>default: none |\n| headers | map of HTTP headers which will be sent together with the request | **O** | **map**  <br>default: none |\n| data | data sent as HTTP request body. If not specified, template payload data is sent | **O** | **string**  <br>default: template data |\n| selector | JSON pointer to property or object from received response that will be returned as response | **O** | **string**  <br>default: none |\n| filter | filter results based on the property and given value (eg. filtering an array of results by items property value) | **O** | **filter object**  <br>default: none |\n| failOnError | array of fail conditions to check and act upon. By default fails cause an error to be thrown. | **O** | **array of failOnError objects**  <br>default: none |\n\n###### http command example\n\n``` json\n{\n    \"type\": \"HTTP\",\n    \"condition\": \"\",\n    \"operation\": \"\",\n    \"action\": \"\",\n    \"description\": \"\",\n    \"method\": \"\",\n    \"url\": \"\",\n    \"headers\": {\n        \"< Map of HTTP headers to send with the request >\"\n    },\n    \"data\": \"\",\n    \"selector\": \"\",\n    \"filter\": {\n        \"property\": \"\",\n        \"value\": \"\"\n    },\n    \"failOnError\": [{\n        \"condition\": \"\",\n        \"action\": \"\"\n    }]\n}\n\n ```\n\n##### authentication\n\n``` json\n{\n    \"type\": \"\",\n    \"token\": \"\",\n    \"username\": \"\",\n    \"password\": \"\"\n}\n\n ```\n\n##### filter\n\n``` json\n{\n          \"property\": \"\",\n          \"value\": \"\"\n}\n\n ```\n\n##### failOnError\n\n``` json\n[\n        {\n            \"condition\": \"\",\n            \"action\": \"\"\n        }\n]\n\n ```\n\n- **condition:** Jinja expression, which if evaluated to **true**, acts according to the action\n    \n- **action:** one of **ERROR**, **RETRY** or **IGNORE**  \n    \\- **ERROR**: throws an error. This is the default behavior.  \n    \\- **RETRY**: throws a specific error which will cause RETRY response.  \n    \\- **IGNORE**: acts as if the command succeeded\n    \n\n#### Placeholders\n\nCurrently several types of special placeholders are recognized by BLS.\n\n| **Placeholder** | **Description** |\n| --- | --- |\n| **REQUEST** | These placeholders for accessing request data. |\n| **OBAPI** | These placeholders are looked up and replaced with data from OBAPI request. (It is an alias for REQUEST to stay compatible with currently existing pipeline configuration templates)  <br>**Example**: OBAPI.subscriber.subscriberName |\n| **CONFIG** | These placeholders are looked up and replaced with data from configuration file(s) and environment variables.  <br>**Example**: CONFIG.sdn.smxHost |\n| **AUTHCODE** | These placeholders are looked up and replaced with data from the authorization code list of OBAPI (parsed from OBAPI request object **authorization.authorizationCodes**). |\n| **DEFAULT** | These placeholders are looked up and replaced with data from a separate configuration found in database collection **PipelineData** and contains default values usable in template. |\n| **CURRENT** | These placeholders are looked up from the result of a previous command. |\n| **LASTPROVISIONED** | These placeholders contain data from last provisioned state of currently processed entity (device/subscriber/phone …etc). |\n| **SESSION** | Special placeholders to access login/authentication information gathered from a session command which is usually needed if the remote API requires some kind of authentication for subsequent requests. |\n| **RESULT** | These placeholders provide access to results of all previously executed stages. |\n| **ARGS** | Special placeholders which contains arguments supplied to template function **execute**, more information about command libraries and library command execution. |\n| **ITERATION** | Special placeholders used in logic profiles which support **iterateOver** functionality (currently only SIMPLE logic profile). It contains data of the item in current loop iteration. |\n\n#### Placeholder Validation\n\nIt is possible to validate placeholders used in templates. You can use the **validate** filter. When validation fails a ValidationException is thrown, which is to be handled in upper levels of application.\n\n##### Available validation types\n\n- Validate that the placeholder is present and contains non-null value\n    \n\n```\n{{ some.placeholder | validate }}\n\n ```\n\n- **DECIMAL** - Validate that the placeholder contains a valid decimal value, optionally checking that it’s within specified boundary (including min and max values)\n    \n\n```\n{{ some.placeholder | validate('DECIMAL') }}\n{{ some.placeholder | validate('DECIMAL', 10, 20) }}\n\n ```\n\n- **FLOAT** - Validate that the placeholder contains a valid floating point number, optionally check that it’s within specified boundary (including min and max values)\n    \n\n```\n{{ some.placeholder | validate('FLOAT') }}\n{{ some.placeholder | validate('FLOAT', 0.0, 0.99) }}\n\n ```\n\n- **STRING** - Validate that the placeholder is a string, optionally checking that its length is within specified boundaries\n    \n\n```\n{{ some.placeholder | validate('STRING') }}\n{{ some.placeholder | validate('STRING', 0, 255) }}\n\n ```\n\n- **REGEX** - Validate that the placeholder matches a regular expression\n    \n\n```\n{{ some.placeholder | validate('REGEX', '[a-z]+') }}\n\n ```\n\nIt is also possible to chain the validation filters:\n\n```\n{{ some.placeholder | validate('STRING', 0, 255) | validate('REGEX', '[a-zA-Z ]+') }}\n\n ```\n\n#### Conditional Provisioning\n\nIt is possible to conditionally allow or disable a pipeline configuration stage by using the enabled element of the root Pipeline element.\n\n``` json\n{\n        \"enabled\": \"JINJA EXPRESSION\",\n        \"nameVersion\": {},       \n        \"description\": \" ... \",\n        \"contentType\": \" ... \",\n        \"provisionType\": \" ... \",\n        \"template\": { ... }\n}\n\n ```\n\nWhen the expression is evaluated to:\n\n- **true** - logic from the golden configuration is executed\n    \n- **false** - the whole golden configuration processing is skipped without error\n    \n\n**JINJA EXPRESSION** is any Jinja2 expression that evaluates to any of these:\n\n- boolean value (true or false)\n    \n- number: **0** means **false**, any other value means **true**\n    \n- string: \"**1**\", \"**true**\", \"**yes**\", \"**on**\", \"**enabled**\" evaluates to **true**\n    \n- string: \"**0**\", \"**false**\", \"**no**\", \"**off**\", \"**disabled**\" evaluates to **false**\n    \n- any other outcome is considered as **true** and a warning is logged about unknown/wrong expression\n    \n\nWhen this property is not specified, it’s considered as **true** by default, meaning that the golden configuration logic will be processed.\n\n#### JINJA\n\nA Jinja template is simply a text file. Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn’t need to have a specific extension: `.html`, `.xml`, or any other extension is just fine.\n\nA template contains **variables** and/or **expressions**, which get replaced with values when a template is _rendered_; and **tags**, which control the logic of the template. The template syntax is heavily inspired by Django and Python.\n\n##### JINJA Delimiters\n\n- `{% ... %}` for [Statements](https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-control-structures)\n    \n- `{{ ... }}` for [Expressions](https://jinja.palletsprojects.com/en/3.0.x/templates/#expressions) to print to the template output\n    \n- `{# ... #}` for [Comments](https://jinja.palletsprojects.com/en/3.0.x/templates/#comments) not included in the template output\n    \n\n[Line Statements and Comments](https://jinja.palletsprojects.com/en/3.0.x/templates/#line-statements) are also possible, though they don’t have default prefix characters. To use them, set `line_statement_prefix` and `line_comment_prefix` when creating the [<code><b>Environment</b></code>](https://jinja.palletsprojects.com/en/3.0.x/api/#jinja2.Environment).\n\n[https://jinja.palletsprojects.com/en/3.0.x/templates/](https://jinja.palletsprojects.com/en/3.0.x/templates/)\n\n#### Global Variables\n\nTo have variables accessible anywhere in your templates, commands and stages, you can define them as **Global Variables.** To do so, there are two ways to do it:\n\n##### Template\n\n``` json\n{\n    \"schema_version\": \"3.0\",\n    \"nameVersion\": { ... },\n    \"description\": \" ... \",\n    \"alias\": \" ... \",\n    \"template\": {\n        \"commands\": [ ... ],\n        \"variables\": {\n            \"stringVar\": \"string variable\",\n            \"numberVar\": 10,\n            \"booleanVar\": true,\n            \"arrayVar\": [ \"a\", \"b\", \"c\" ],\n            \"objVar\": {\n                \"prop1\": \"property one\",\n                \"prop2\": true,\n                \"prop3\": [ 1, 2, 3, 4 ]\n            }\n        }\n    }\n}\n\n ```\n\n##### JINJA Snippet\n\n```\n# gset usage:\n{% gset name = value %}\n# example\n{% gset deviceId = REQUEST.device.deviceId %}\n\n ```\n\n##### Global variable name restrictions\n\nGlobal variables are stored in global pipeline context and to make sure they don’t interfere with placeholder values, the following name restrictions are enforced:\n\n- variable name cannot be all capital letters (doesn’t matter if it contains underscores or not)\n    \n- variable name must be composed from letters: **a-z**, **A-Z**, **0-9** and **underscore**\n    \n\n#### Managing Authentication Tokens\n\nReadyLinks Example\n\n``` json\n{\n    \"username\" : \"{{ CONFIG.sdn.readyLinksUserName }}\",\n    \"password\" : \"{{ CONFIG.sdn.readyLinks.Password}}\"\n}\n\n ```\n\nresponse\n\n``` json\n{\n    \"access_token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDYyNDM3MjQsImlhdCI6MTYwNjI0MzQyNCwibmJmIjoxNjA2MjQzNDI0LCJpZGVudGl0eSI6ImR0aWRkQGV0aXNvZnR3YXJlLmNvbSJ9.DjWgvFjL5eYsp_QTZjbk-e-ZnsWfDK6VzSGZJMN_Qhk\"\n}\n\n ```\n\nAccess by other Stages\n\n```\n{{ SESSION['readyLinks']['TOKEN'] }}\n\n ```\n\n## Examples\n\n#### Calix AXOS SMx ONT-716GE\n\n``` json\n{\n    \"schema_version\": \"3.0\",\n    \"nameVersion\": {\n        \"name\": \"ONT-716GE\",\n        \"version\": \"3.0\",\n        \"sequence\": 1,\n        \"delSequence\": 4\n    },\n    \"description\": \"CalixAXOS Subscriber account configuration\",\n    \"contentType\": \"application/json\",\n    \"provisionType\": \"FTTH\",\n    \"template\": {\n        \"variables\": {\n            \"accountId\": \"{{ AUTHCODE['CALIX-ACCOUNT-ID'] }}\",\n            \"subscriberName\": \"{{ OBAPI.subscriber.subscriberName }}\",\n            \"address1\": \"{{ OBAPI.location.address1 }}\",\n            \"city\": \"{{ OBAPI.location.city }}\",\n            \"state\": \"{{ OBAPI.location.state }}\",\n            \"zip\": \"{{ OBAPI.location.zip }}\",\n            \"contactPhone\": \"{{ OBAPI.subscriber.contactPhone }}\",\n            \"emailAddress\": \"{{ OBAPI.subscriber.emailAddress }}\",\n            \"createUpdatePayload\": \"\n                    {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"name\\\": \\\"{{ subscriberName }}\\\",\\\"customId\\\": \\\"{{ accountId }}\\\",\\\"orgId\\\": \\\"Calix\\\",\n                    \\\"locations\\\": {\\\"description\\\": \\\"\\\",\\\"primary\\\": true,\\\"address\\\": {\\\"streetLine1\\\": \\\"{{ address1 }}\\\",\\\"city\\\": \\\"{{ city }}\\\",\n                    \\\"state\\\": \\\"{{ state }}\\\",\\\"zip\\\": \\\"{{ zip }}\\\",\\\"country\\\": \\\"United States\\\"},\\\"contacts\\\": {\\\"phone\\\": \\\"{{ contactPhone }}\\\",\n                    \\\"email\\\": \\\"{{ emailAddress }}\\\",\\\"primary\\\": true}}}\"\n        },\n        \"data\": \"\n          {\\\"name\\\": \\\"{{ subscriberName }}\\\",\\\"customId\\\": \\\"{{ accountId }}\\\",\\\"orgId\\\": \\\"Calix\\\",\\\"locations\\\": [{\\\"description\\\": \\\"\\\",\\\"primary\\\": true,\n            \\\"address\\\": [{\\\"streetLine1\\\": \\\"{{ address1 }}\\\",\\\"city\\\": \\\"{{ city }}\\\",\\\"state\\\": \\\"{{ state }}\\\",\\\"zip\\\": \\\"{{ zip }}\\\",\n            \\\"country\\\": \\\"United States\\\"}],\\\"contacts\\\": [{\\\"phone\\\": \\\"{{ contactPhone }}\\\",\\\"email\\\": \\\"{{ emailAddress }}\\\",\\\"primary\\\": true}]}]}\",\n        \"commands\": [\n            {\n                \"operation\": \"READ\",\n                \"alias\": \"GetSUBSCRIBER\",\n                \"description\": \"Get CalixAXOS subscriber record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.getEmsSubscriberOrgAccountByAccountName\",\n                \"data\": \"{% set accountId = 'UNKNOWN' %}\n                  {% if AUTHCODE['CALIX-ACCOUNT-ID'] is defined and AUTHCODE['CALIX-ACCOUNT-ID'] | length %}\n                    {% set accountId = AUTHCODE['CALIX-ACCOUNT-ID'] %}\n                  {% elif LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] is defined and LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] | length  %}\n                    {% set accountId = LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] %}\n                    {{ log( 'CalixAXOS SUBSCRIBER: QUERY using LASTPROVISIONED CALIX-ACCOUNT-ID={}', accountId ) }}\n                  {% endif %}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"orgId\\\": \\\"Calix\\\",\\\"accountName\\\": \\\"{{ accountId }}\\\"}\"\n            },\n            {\n                \"operation\": \"CREATE\",\n                \"description\": \"Create new CalixAXOS subscriber record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.createEmsSubscriber\",\n                \"data\": \"{{ log( 'CalixAXOS SUBSCRIBER: Send CREATE: accountId={}', accountId ) }}\\n{{ createUpdatePayload }}\"\n            },\n            {\n                \"operation\": \"UPDATE\",\n                \"description\": \"Update CalixAXOS subscriber record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.updateEmsSubscriber\",\n                \"data\": \"{{ log( 'CalixAXOS SUBSCRIBER: Send UPDATE: accountId={}', accountId ) }}\\n{{ createUpdatePayload }}\"\n            },\n            {\n                \"operation\": \"DELETE\",\n                \"description\": \"Delete CalixAXOS subscriber record\",\n                \"condition\": \"{#                                                  #}\n                  {# Do not delete SUBSCRIBER if accountId is not set #}\n                  {#                                                  #}\n                  {% set accountId = 'UNKNOWN' %}\n                  {% if AUTHCODE['CALIX-ACCOUNT-ID'] is defined and AUTHCODE['CALIX-ACCOUNT-ID'] | length %}\n                    {% set accountId = AUTHCODE['CALIX-ACCOUNT-ID'] %}\n                  {% elif LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] is defined and LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] | length  %}\n                    {% set accountId = LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] %}\n                  {% endif %}\n                  {#                                                              #}\n                  {# Do not delete SUBSCRIBER if there are active sibling devices #}\n                  {#                                                              #}\n                  {% set siblingList = [] %}\n                  {% for sibling in OBAPI.siblingDevices %}\n                    {% if not( sibling.subscriber is none ) %}\n                      {% set result = siblingList.append(sibling) %}\n                    {% endif %}\n                  {% endfor %}\n                  {% set siblingCount = siblingList|count %}\n                  {#                                                               #}\n                  {# Do not send delete command if CURRENT SUBSCRIBER is not found #}\n                  {#                                                               #}\n                  {% set currentCustomId = CURRENT.GetSUBSCRIBER['customId'] %}\n                  {% if accountId != 'UNKNOWN' and siblingCount == 0 and currentCustomId|length > 0 %}\n                    {{ log( 'CalixAXOS SUBSCRIBER: Send DELETE: accountId={}', accountId ) }}\n                  {% else %}\n                    {% if siblingCount > 0 %}\n                      {{ log( 'CalixAXOS SUBSCRIBER: DELETE: Do not delete subscriber - SiblingCount={}', siblingCount ) }}\n                    {% endif %}\n                    {% if currentCustomId|length == 0 %}\n                      {{ log( 'CalixAXOS SUBSCRIBER: DELETE: No need to send delete - CURRENT not found' ) }}\n                    {% endif %}\n                  {% endif %}\n                  {#                 #}\n                  {# Check condition #}\n                  {#                 #}\n                  {{ accountId != 'UNKNOWN' and siblingCount == 0 and currentCustomId|length > 0 }}\",\n                \"delayBefore\": 10000,\n                \"failOnError\": false,\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.deleteEmsSubscriberOrgAccountByAccountName\",\n                \"data\": \"{% set accountId = 'UNKNOWN' %}\n                  {% if AUTHCODE['CALIX-ACCOUNT-ID'] is defined and AUTHCODE['CALIX-ACCOUNT-ID'] | length %}\n                    {% set accountId = AUTHCODE['CALIX-ACCOUNT-ID'] %}\n                  {% elif LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] is defined and LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] | length  %}\n                    {% set accountId = LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] %}\n                  {% endif %}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"orgId\\\": \\\"Calix\\\",\\\"accountName\\\": \\\"{{ accountId }}\\\",\\\"forceDelete\\\": true}\"\n            },\n            {\n                \"operation\": \"DELETE\",\n                \"description\": \"Delete CalixAXOS subscriber record for a 2nd time\",\n                \"condition\": \"{#                                                  #}\n                  {# Do not delete SUBSCRIBER if accountId is not set #}\n                  {#                                                  #}\n                  {% set accountId = 'UNKNOWN' %}\n                  {% if AUTHCODE['CALIX-ACCOUNT-ID'] is defined and AUTHCODE['CALIX-ACCOUNT-ID'] | length %}\n                    {% set accountId = AUTHCODE['CALIX-ACCOUNT-ID'] %}\n                  {% elif LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] is defined and LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] | length  %}\n                    {% set accountId = LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] %}\n                  {% endif %}\n                  {#                                                              #}\n                  {# Do not delete SUBSCRIBER if there are active sibling devices #}\n                  {#                                                              #}\n                  {% set siblingList = [] %}\n                  {% for sibling in OBAPI.siblingDevices %}\n                    {% if not( sibling.subscriber is none ) %}\n                      {% set result = siblingList.append(sibling) %}\n                    {% endif %}\n                  {% endfor %}\n                  {% set siblingCount = siblingList|count %}\n                  {#                                                               #}\n                  {# Do not send delete command if CURRENT SUBSCRIBER is not found #}\n                  {#                                                               #}\n                  {% set currentCustomId = CURRENT.GetSUBSCRIBER['customId'] %}\n                  {#                 #}\n                  {# Check condition #}\n                  {#                 #}\n                  {{ accountId != 'UNKNOWN' and siblingCount == 0 and currentCustomId|length > 0 }}\",\n                \"delayBefore\": 10000,\n                \"failOnError\": false,\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.deleteEmsSubscriberOrgAccountByAccountName\",\n                \"data\": \"{% set accountId = 'UNKNOWN' %}\n                  {% if AUTHCODE['CALIX-ACCOUNT-ID'] is defined and AUTHCODE['CALIX-ACCOUNT-ID'] | length %}\n                    {% set accountId = AUTHCODE['CALIX-ACCOUNT-ID'] %}\n                  {% elif LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] is defined and LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] | length  %}\n                    {% set accountId = LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] %}\n                  {% endif %}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"orgId\\\": \\\"Calix\\\",\\\"accountName\\\": \\\"{{ accountId }}\\\",\\\"forceDelete\\\": true}\"\n            }\n        ]\n    }\n}\n\n ```\n\n``` json\n{\n    \"schema_version\": \"3.0\",\n    \"nameVersion\": {\n        \"name\": \"ONT-716GE\",\n        \"version\": \"3.0\",\n        \"sequence\": 2,\n        \"delSequence\": 3\n    },\n    \"description\": \"CalixAXOS ONT configuration\",\n    \"contentType\": \"application/json\",\n    \"provisionType\": \"FTTH\",\n    \"template\": {\n        \"variables\": {\n            \"accountId\": \"{{ AUTHCODE['CALIX-ACCOUNT-ID'] }}\",\n            \"address1\": \"{{ OBAPI.location.address1 }}\",\n            \"deviceModel\": \"{{ OBAPI.device.deviceModel }}\",\n            \"ontProfileId\": \"{{ deviceModel.split(\\\" \\\", 3)[0] }}\",\n            \"unitAddress\": \"{{ OBAPI.device.unitAddress }}\",\n            \"sid\": \"{{ unitAddress.split(\\\":\\\", 2)[0] }}\",\n            \"ontId\": \"{{ unitAddress.split(\\\":\\\", 2)[1] }}\",\n            \"ontType\": \"Residential\",\n            \"serialNumber\": \"{{ OBAPI.ont.serialNumber }}\"    \n        },\n        \"data\": \"\n          {\\\"description\\\": \\\"{{ address1 }}\\\",\\\"ont-id\\\": \\\"{{ ontId }}\\\",\\\"ont-type\\\": \\\"{{ ontType }}\\\",\n            \\\"subscriber-id\\\": \\\"{{ accountId }}\\\",\\\"ont-reg-id\\\": \\\"{{ serialNumber }}\\\",\\\"ont-profile-id\\\": \\\"{{ ontProfileId }}\\\"}\",\n        \"commands\": [\n            {\n                \"operation\": \"READ\",\n                \"alias\": \"GetONT\",\n                \"description\": \"Get CalixAXOS ONT record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.listConfigDeviceOnt\",\n                \"data\": \"{\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"ontId\\\": \\\"{{ ontId }}\\\"}\"\n            },\n            {\n                \"operation\": \"CREATE\",\n                \"description\": \"Create new CalixAXOS ONT record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.createConfigDeviceOnt\",\n                \"data\": \"{{ log( 'CalixAXOS ONT: Send CREATE: ontId={} accountId={}', ontId, accountId ) }}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName1\\\": \\\"{{ sid }}\\\",\\\"description\\\": \\\"{{ address1 }}\\\",\n                    \\\"ontId\\\": \\\"{{ ontId }}\\\",\\\"ontType\\\": \\\"{{ ontType }}\\\",\\\"subscriberId\\\": \\\"{{ accountId }}\\\",\n                    \\\"ontRegId\\\": \\\"{{ serialNumber }}\\\",\\\"ontProfileId\\\": \\\"{{ ontProfileId }}\\\"}\",\n                \"delayAfter\": 10000\n            },\n            {\n                \"operation\": \"UPDATE\",\n                \"description\": \"Update CalixAXOS ONT record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.updateConfigDeviceOnt\",\n                \"data\": \"{{ log( 'CalixAXOS ONT: Send UPDATE: ontId={} accountId={}', ontId, accountId ) }}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName1\\\": \\\"{{ sid }}\\\",\\\"ontId1\\\": \\\"{{ ontId }}\\\",\n                    \\\"description\\\": \\\"{{ address1 }}\\\",\\\"ontId\\\": \\\"{{ ontId }}\\\",\\\"ontType\\\": \\\"{{ ontType }}\\\",\n                    \\\"subscriberId\\\": \\\"{{ accountId }}\\\",\\\"ontRegId\\\": \\\"{{ serialNumber }}\\\",\\\"ontProfileId\\\": \\\"{{ ontProfileId }}\\\",\n                    \\\"deviceName\\\": \\\"{{ CURRENT.GetONT['device-name'] }}\\\"}\"\n            },\n            {\n                \"operation\": \"DELETE\",\n                \"description\": \"Delete CalixAXOS ONT record\",\n                \"condition\": \"{#                                           #}\n                  {# Do not delete ONT if accountId is not set #}\n                  {#                                           #}\n                  {% set accountId = AUTHCODE['CALIX-ACCOUNT-ID'] %}\n                  {% if accountId is none or accountId == '' %}\n                    {#                                                   #}\n                    {# Retrieve accountId from LASTPROVISIONED authCodes #}\n                    {#                                                   #}\n                    {% set accountId = 'UNKNOWN' %}\n                    {% if AUTHCODE['CALIX-ACCOUNT-ID'] is defined and AUTHCODE['CALIX-ACCOUNT-ID'] | length %}\n                      {% set accountId = AUTHCODE['CALIX-ACCOUNT-ID'] %}\n                    {% elif LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] is defined and LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] | length  %}\n                      {% set accountId = LASTPROVISIONED.authCode['CALIX-ACCOUNT-ID'] %}\n                    {% endif %}\n                    {#                                                       #}\n                    {# Do not delete ONT if there are active sibling devices #}\n                    {#                                                       #}\n                    {% set siblingList = [] %}\n                    {% for sibling in OBAPI.siblingDevices %}\n                     {% if not( sibling.subscriber is none ) %}\n                      {% set result = siblingList.append(sibling) %}\n                     {% endif %}\n                    {% endfor %}\n                    {% set siblingCount = siblingList|count %}\n                    {#                                                        #}\n                    {# Do not send delete command if CURRENT ONT is not found #}\n                    {#                                                        #}\n                    {% set currentOntId = CURRENT.GetONT['ont-id'] %}\n                    {#                                                #}\n                    {# Log whether or not delete command will be sent #}\n                    {#                                                #}\n                    {% if accountId != 'UNKNOWN' and siblingCount == 0 and currentOntId|length > 0 %}\n                      {% set unitAddress = OBAPI.device.unitAddress %}\n                      {% set unitAddressSplit = unitAddress.split(\\\":\\\", 2) %}\n                      {% set sid = unitAddressSplit[0] %}\n                      {% set ontId = unitAddressSplit[1] %}\n                      {{ log( 'CalixAXOS ONT: Send DELETE: ontId={}', ontId ) }}\n                    {% else %}\n                    {% if siblingCount > 0 %}\n                      {{ log( 'CalixAXOS ONT: DELETE: Do not delete ONT - SiblingCount={}', siblingCount ) }}\n                    {% endif %}\n                    {% if currentOntId|length == 0 %}\n                      {{ log( 'CalixAXOS ONT: DELETE: No need to send delete - CURRENT not found' ) }}\n                    {% endif %}\n                  {% endif %}\n                  {#                 #}\n                  {# Check condition #}\n                  {#                 #}\n                  {{ accountId != 'UNKNOWN' and siblingCount == 0 and currentOntId|length > 0 }}\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.deleteConfigDeviceOnt\",\n                \"failOnError\": false,\n                \"data\": \"{\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"ontId\\\": \\\"{{ ontId }}\\\"}\"\n            }\n        ]\n    }\n}\n\n ```\n\n``` json\n{\n    \"schema_version\": \"3.0\",\n    \"nameVersion\": {\n        \"name\": \"ONT-716GE\",\n        \"version\": \"3.0\",\n        \"sequence\": 3,\n        \"delSequence\": 2\n    },\n    \"description\": \"CalixAXOS ONTPORT configuration\",\n    \"contentType\": \"application/json\",\n    \"provisionType\": \"FTTH\",\n    \"enabled:\" \"{% set portType = OBAPI.device.deviceId[-3:1] %}{% portType!='P' and portType!='V' %}\"\n    \"template\": {\n        \"variables\": {\n            \"accountId\": \"{{ AUTHCODE['CALIX-ACCOUNT-ID'] }}\",\n            \"serviceName\": \"{{ AUTHCODE['SERVICENAME'] }}\",\n            \"serviceDescription\": \"{{ AUTHCODE['SERVICEDESCRIPTION'] }}\",\n            \"dataPolicyMap\": \"{{ AUTHCODE['DATA-POLICY-MAP'] }}\",\n            \"deviceId\": \"{{ OBAPI.device.deviceId }}\",\n            \"deviceSuffix\": \"{{ deviceId[-3:] }}\",\n            \"portType\": \"{{ deviceSuffix[0:1] | lower }}\",\n            \"portIndex\": {{ deviceSuffix[1:3] | int }},\n            \"ontPortId\": \"{{ portType ~ portIndex }}\",\n            \"unitAddress\": \"{{ OBAPI.device.unitAddress }}\",\n            \"unitAddressSplit\": \"{{ unitAddress.split(\\\":\\\", 2) }}\",\n            \"sid\": \"{{ unitAddressSplit[0] }}\",\n            \"ontId\": \"{{ unitAddressSplit[1] }}\",\n            \"adminState\": \"enabled\"            \n        },\n        \"data\": \"{\\\"subscriber-id\\\": \\\"{{ accountId }}\\\",\\\"device-name\\\": \\\"{{ sid }}\\\",\\\"ont-id\\\": \\\"{{ ontId }}\\\",\\\"ont-port-id\\\": \\\"{{ ontPortId }}\\\",\n            \\\"service-name\\\": \\\"{{ serviceName }}\\\",\\\"description\\\": \\\"{{ serviceDescription }}\\\",\\\"subscriber-id\\\": \\\"{{ accountId }}\\\",\n            \\\"policy-map\\\": \\\"{{ dataPolicyMap }}\\\",\\\"admin-state\\\": \\\"{{ adminState }}\\\"}\",\n        \"immutables\": [\n            {\n                \"path\": \"/policy-map\",\n                \"description\": \"Policy Map Name\"\n            },\n            {\n                \"path\": \"/multicast-profile\",\n                \"description\": \"Multicast Profile\"\n            },\n            {\n                \"path\": \"/vlan\",\n                \"description\": \"VLAN\"\n            }\n        ],\n        \"commands\": [\n            {\n                \"operation\": \"READ\",\n                \"alias\": \"GetONTPORT\",\n                \"description\": \"Get CalixAXOS ONTPORT service record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.listEmsService\",\n                \"data\": \"{\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"ontId\\\": \\\"{{ ontId }}\\\",\\\"ontPortId\\\": \\\"{{ ontPortId }}\\\"}\"\n            },\n            {\n                \"operation\": \"CREATE\",\n                \"description\": \"Create new CalixAXOS ONTPORT service record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.createEmsService\",\n                \"data\": \"{{ log( 'CalixAXOS Internet ONTPORT: Send CREATE: ontId={} ontPortId={}', ontId, ontPortId ) }}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName1\\\": \\\"{{ sid }}\\\",\\\"ontId1\\\": \\\"{{ ontId }}\\\",\\\"ontPortId1\\\": \\\"{{ ontPortId }}\\\",\n                    \\\"subscriberId\\\": \\\"{{ accountId }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"serviceName\\\": \\\"{{ serviceName }}\\\",\n                    \\\"description\\\": \\\"{{ serviceDescription }}\\\",\\\"subscriberId\\\": \\\"{{ accountId }}\\\",\\\"policyMap\\\": \\\"{{ dataPolicyMap }}\\\",\n                    \\\"adminState\\\": \\\"{{ adminState }}\\\"}\"\n            },\n            {\n                \"operation\": \"UPDATE\",\n                \"description\": \"Update CalixAXOS ONTPORT service record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.updateEmsService\",\n                \"data\": \"{{ log( 'CalixAXOS Internet ONTPORT: Send UPDATE: ontId={} ontPortId={}', ontId, ontPortId ) }}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName1\\\": \\\"{{ sid }}\\\",\\\"ontId1\\\": \\\"{{ ontId }}\\\",\\\"ontPortId1\\\": \\\"{{ ontPortId }}\\\",\n                    \\\"subscriberId\\\": \\\"{{ accountId }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"serviceName\\\": \\\"{{ serviceName }}\\\",\\\"description\\\": \\\"{{ serviceDescription }}\\\",\n                    \\\"subscriberId\\\": \\\"{{ accountId }}\\\",\\\"policyMap\\\": \\\"{{ dataPolicyMap }}\\\",\\\"adminState\\\": \\\"{{ adminState }}\\\"}\"\n            },\n            {\n                \"operation\": \"DELETE\",\n                \"description\": \"Delete CalixAXOS Internet ONTPORT service\",\n                \"condition\": \"{% set currentOntId = CURRENT.GetONTPORT['ont-id'] %}\n                  {% if currentOntId|length == 0 %}\n                    {{ log( 'CalixAXOS Internet ONTPORT: DELETE: No need to send delete - CURRENT not found' ) }}\n                  {% endif %}\n                  {{ currentOntId|length > 0 }}\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.deleteEmsService\",\n                \"data\": \"\n                  {% set currentOntId = CURRENT.GetONTPORT['ont-id'] %}\n                  {% if currentOntId|length > 0 %}\n                    {{ log( 'CalixAXOS Internet ONTPORT: Send DELETE: ontId={} ontPortId={}', ontId, ontPortId ) }}\n                  {% endif %}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"ontId\\\": \\\"{{ ontId }}\\\",\\\"ontPortId\\\": \\\"{{ ontPortId }}\\\"}\"\n            }\n        ]\n    }\n}\n\n ```\n\n``` json\n{\n    \"schema_version\": \"3.0\",\n    \"nameVersion\": {\n        \"name\": \"ONT-716GE\",\n        \"version\": \"3.0\",\n        \"sequence\": 4,\n        \"delSequence\": 1\n    },\n    \"description\": \"CalixAXOS SIP ONTPORT configuration\",\n    \"contentType\": \"application/json\",\n    \"provisionType\": \"FTTH\",\n    \"enabled:\" \"{% set portType = OBAPI.device.deviceId[-3:1] %}{% portType=='P' %}\"\n    \"template\": {\n        \"variables\": {\n            \"accountId\": \"{{ AUTHCODE['CALIX-ACCOUNT-ID'] }}\",\n            \"serviceName\": \"SIP\",\n            \"serviceDescription\": \"{{ AUTHCODE['SERVICEDESCRIPTION'] }}\",\n            \"phonePolicyMap\": \"{{ AUTHCODE['PHONE-POLICY-MAP'] }}\",\n            \"phoneVlanId\": \"{{ AUTHCODE['PHONE-VLAN-ID'] }}\",\n            \"sipProfile\": \"{{ AUTHCODE['SIP-PROFILE'] }}\",\n            \"dialPlan\": \"Dial_Plan\",\n            \"sipUsername\": \"{{ AUTHCODE['SIP-USERNAME'] }}\",\n            \"sipPassword\": \"{{ AUTHCODE['SIP-PASSWORD'] }}\",\n            \"sipUri\": \"{{ AUTHCODE['SIP-USERNAME'] }}\",\n            \"deviceId\": \"{{ OBAPI.device.deviceId }}\",\n            \"deviceSuffix\": \"{{ deviceId[-3:] }}\",\n            \"portType\": \"{{ deviceSuffix[0:1] | lower }}\",\n            \"portIndex\": {{ deviceSuffix[1:3] | int }},\n            \"ontPortId\": \"{{ portType ~ portIndex }}\",\n            \"unitAddress\": \"{{ OBAPI.device.unitAddress }}\",\n            \"unitAddressSplit\": \"{{ unitAddress.split(\\\":\\\", 2) }}\",\n            \"sid\": \"{{ unitAddressSplit[0] }}\",\n            \"ontId\": \"{{ unitAddressSplit[1] }}\",\n            \"adminState\": \"enabled\"\n        },\n        \"data\": \"{\\\"subscriber-id\\\": \\\"{{ accountId }}\\\",\\\"device-name\\\": \\\"{{ sid }}\\\",\\\"ont-id\\\": \\\"{{ ontId }}\\\",\\\"ont-port-id\\\": \\\"{{ ontPortId }}\\\",\n            \\\"service-name\\\": \\\"{{ serviceName }}\\\",\\\"description\\\": \\\"{{ serviceDescription }}\\\",\\\"policy-map\\\": \\\"{{ phonePolicyMap }}\\\",\n            \\\"vlan\\\": \\\"{{ phoneVlanId }}\\\",\\\"sip-profile\\\": \\\"{{ sipProfile }}\\\",\\\"dial-plan\\\": \\\"{{ dialPlan }}\\\",\\\"user\\\": \\\"{{ sipUsername }}\\\",\n            \\\"password\\\": \\\"{{ sipPassword }}\\\",\\\"uri\\\": \\\"{{ sipUri }}\\\",\\\"admin-state\\\": \\\"{{ adminState }}\\\"}\",\n        \"immutables\": [\n            {\n                \"path\": \"/policy-map\",\n                \"description\": \"Policy Map Name\"\n            },\n            {\n                \"path\": \"/multicast-profile\",\n                \"description\": \"Multicast Profile\"\n            },\n            {\n                \"path\": \"/vlan\",\n                \"description\": \"VLAN\"\n            }\n        ],\n        \"commands\": [\n            {\n                \"operation\": \"READ\",\n                \"alias\": \"GetONTPORT\",\n                \"description\": \"Get CalixAXOS ONTPORT service record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.listEmsService\",\n                \"data\": \"{\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"ontId\\\": \\\"{{ ontId }}\\\",\\\"ontPortId\\\": \\\"{{ ontPortId }}\\\"}\"\n            },\n            {\n                \"operation\": \"CREATE\",\n                \"description\": \"Create new CalixAXOS ONTPORT service record\",\n                \"condition\": \"{#                                                        #}{# Only send CREATE command if SIP URI field is populated #}{#                                                        #}{% set sipUri = AUTHCODE['SIP-USERNAME'] %}{% if sipUri|length == 0 %}{{ log( 'CalixAXOS SIP ONTPORT: Do not send CREATE command - Phone line is not assigned' ) }}{% endif %}{{ sipUri|length > 0 }}\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.createEmsService\",\n                \"data\": \"\n                  {% if sipUri|length > 0 %}\n                    {{ log( 'CalixAXOS SIP ONTPORT: Send CREATE: ontId={} ontPortId={}', ontId, ontPortId ) }}\n                  {% endif %}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName1\\\": \\\"{{ sid }}\\\",\\\"ontId1\\\": \\\"{{ ontId }}\\\",\\\"ontPortId1\\\": \\\"{{ ontPortId }}\\\",\n                    \\\"subscriberId\\\": \\\"{{ accountId }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"serviceName\\\": \\\"{{ serviceName }}\\\",\\\"description\\\": \\\"{{ serviceDescription }}\\\",\n                    \\\"policyMap\\\": \\\"{{ phonePolicyMap }}\\\",\\\"vlan\\\": \\\"{{ phoneVlanId }}\\\",\\\"sipProfile\\\": \\\"{{ sipProfile }}\\\",\\\"dialPlan\\\": \\\"{{ dialPlan }}\\\",\n                    \\\"user\\\": \\\"{{ sipUsername }}\\\",\\\"password\\\": \\\"{{ sipPassword }}\\\",\\\"uri\\\": \\\"{{ sipUri }}\\\",\\\"adminState\\\": \\\"{{ adminState }}\\\"}\"\n            },\n            {\n                \"operation\": \"UPDATE\",\n                \"description\": \"Update CalixAXOS ONTPORT service record\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.updateEmsService\",\n                \"data\": \"{{ log( 'CalixAXOS SIP ONTPORT: Send UPDATE: ontId={} ontPortId={}', ontId, ontPortId ) }}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName1\\\": \\\"{{ sid }}\\\",\\\"ontId1\\\": \\\"{{ ontId }}\\\",\\\"ontPortId1\\\": \\\"{{ ontPortId }}\\\",\n                    \\\"subscriberId\\\": \\\"{{ accountId }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"serviceName\\\": \\\"{{ serviceName }}\\\",\\\"description\\\": \\\"{{ serviceDescription }}\\\",\n                    \\\"policyMap\\\": \\\"{{ phonePolicyMap }}\\\",\\\"vlan\\\": \\\"{{ phoneVlanId }}\\\",\\\"sipProfile\\\": \\\"{{ sipProfile }}\\\",\\\"dialPlan\\\": \\\"{{ dialPlan }}\\\",\n                    \\\"user\\\": \\\"{{ sipUsername }}\\\",\\\"password\\\": \\\"{{ sipPassword }}\\\",\\\"uri\\\": \\\"{{ sipUri }}\\\",\\\"adminState\\\": \\\"{{ adminState }}\\\"}\"\n            },\n            {\n                \"operation\": \"DELETE\",\n                \"description\": \"Delete CalixAXOS SIP ONTPORT service\",\n                \"condition\": \"{% set currentOntId = CURRENT.GetONTPORT['ont-id'] %}\n                  {% if currentOntId|length == 0 %}\n                    {{ log( 'CalixAXOS SIP ONTPORT: DELETE: No need to send delete - CURRENT not found' ) }}\n                  {% endif %}\n                  {{ currentOntId|length > 0 }}\",\n                \"type\": \"LIBRARY\",\n                \"command\": \"calix.axos.deleteEmsService\",\n                \"data\": \"\n                  {% set currentOntId = CURRENT.GetONTPORT['ont-id'] %}\n                  {% if currentOntId|length > 0 %}\n                    {{ log( 'CalixAXOS SIP ONTPORT: Send DELETE: ontId={} ontPortId={}', ontId, ontPortId ) }}\n                  {% endif %}\n                  {\\\"baseUrl\\\": \\\"{{ CONFIG.calix.smx.url }}\\\",\\\"deviceName\\\": \\\"{{ sid }}\\\",\\\"ontId\\\": \\\"{{ ontId }}\\\",\\\"ontPortId\\\": \\\"{{ ontPortId }}\\\"}\"\n            }\n        ]\n    }\n}\n\n ```\n\n## Event Stream Subscriptions\n\n<img src=\"https://raw.githubusercontent.com/ETI-Software-Solutions/Postman-Collection-Images/main/Webhooks.png\">\n\nThese endpoints are used to manage the Business Logic Service Event Stream Subscriptions.  \nEvents filtered from the stream successfully will be processed by the Business Logic Service generic JSON pipeline processor.  \nOne might think that since WebHooks are real-time events that they are technically difficult to implement. Actually, a key advantage of WebHooks is that they are easier to set up and less resource-intensive than APIs. Creating an API is a complex process that in some cases can be as challenging as designing and building an application itself, but implementing a WebHook simply requires setting up a single POST request on the sending end, establishing a URL on the receiving end to accept the data, then performing some action on the data once it is received.  \nCommon use cases for WebHooks include sending new email list subscriptions and unsubscribe requests to a CRM system, automatically updating accounting software when invoices are paid, or setting up any type of notifications. In each of these types of events, the information flows in one direction, and no request is necessary to receive updated data.  \nThe same characteristics that make WebHooks relatively easy to implement are also the reasons why they are far more limited than APIs. Updating the data a WebHook delivers requires reconfiguring it entirely to listen for a different event, and in most cases it would be more efficient to create a new WebHook. When two systems share data via an API with multiple endpoints, the receiving system has access to a much broader range of data from the sending system. Also, unlike APIs, WebHooks do not allow the sending system to add, update and delete data on the receiving end, which is why WebHooks alone are too limited to offer full integration between two applications.\n\n### Manage Event Stream Subscriptions\n\nManage Event Stream Subscriptions using the Business Logic Service.\n\n##### Calix AXOS SMx New ONT Arrival Event Stream Example\n\n``` json\n{\n    \"name\": \"CalixSMX\",\n    \"type\": \"websocket\",\n    \"contentType\": \"application/json\",\n    \"target\": \"ws://10.1.2.30:18443/rest/v1/alarm/ws?username=admin&password=test123\",\n    \"connectOnStartup\": true,\n    \"reconnect\": true,\n    \"pipeline\": {\n        \"name\": \"CalixSMX-OntArrival\",\n        \"version\": \"1.0\"    },\n    \"filters\":\n    [\n        \"EXISTS\", { \"/ietf-restconf:notification/gpon-interface-base:ont-arrival\": true },\n        [\n            \"AND\", { \"/ietf-restconf:notification/gpon-interface-base:ont-arrival/category\": \"PON\" }\n        ]\n    ]\n}\n\n ```\n\n##### Calix AXOS SMx New ONT Arrival Example Event\n\n``` json\n{\n  deviceTime=1627830868000,\n  receiveTime=1647273168795, \n  severity=INFO, \n  alarmLevel=5, \n  standing=false, \n  alarmReferForClear=5e989af8547555211702aaf6||ont-arrival||6.885, \n  deviceType=E7-2 GPON-8 r2 SIM, \n  sourceType=null, \n  sourceAid=null, \n  category=PON, \n  instanceId=6.885, \n  description=ONT has arrived on PON port, \n  probableCause=ONT has arrived on PON port, \n  details='',\n  SerialNo=CXNK,\n  Vendor-id=CXNK00123442,\n  ONTMAC=00:11:31:00:01:00,\n  MTAMAC=00:11:31:00:01:01,\n  RegId=SA42, \n  deviceSequenceNumber=655, \n  alarm=false, \n  port=1/1/gp1, \n  address=/interfaces/interface[name='1/1/gp1'], \n  primaryElement=null, \n  serviceAffecting=NSA, \n  subscriber=null, \n  isAcked=false, \n  userNotes=null, \n  region=root, \n  ackUser=null, \n  expireAt=null, \n  acked=false, \n  receiveTimeString=2022-03-14T15:52:48, \n  deviceTimeString=2021-08-01T15:14:28, \n  aid=1/1/1/1/gp1, \n  changeString=\"545391924928843776\", \n  condition-type=ont-arrival, \n  device-name=NORCGAXA0L2, \n  shelf-id=1, \n  slot-id=1, \n  port-id=gp1, \n  sequenceNum=545391924928843776, \n  ont-id=null, \n  ont-type=null, \n  ont-port-id=null, \n  pon-system-id=null, \n  admin-partition=null, \n  pon-id=null, \n  equipment-type=ONT, \n  alarm-type=null, \n  switched-pon-id=null, \n  switched-channel-termination=null, \n  pon-device=1.gp1, \n  partition-id=null, \n  switched-shelf=null, \n  switched-slot=null, \n  switched-port=null, \n  serial-number=123442, \n  resource=null\n}\n\n ```\n\n# Contact Email: [info@etisoftware.com](https://mailto:info@etisoftware.com)","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"11528456","team":1088250,"collectionId":"2885587f-fa06-4df0-96ff-a2ceb2733f31","publishedId":"2sA3Bq4B1H","public":true,"publicUrl":"https://bls.etisoftware.com","privateUrl":"https://go.postman.co/documentation/11528456-2885587f-fa06-4df0-96ff-a2ceb2733f31","customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"documentationLayout":"classic-double-column","customisation":{"metaTags":[{"name":"description","value":""},{"name":"title","value":""}],"appearance":{"default":"light","themes":[{"name":"dark","logo":null,"colors":{"top-bar":"212121","right-sidebar":"303030","highlight":"FF6C37"}},{"name":"light","logo":null,"colors":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"}}]}},"version":"8.10.0","publishDate":"2024-08-26T16:51:20.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{"title":"","description":""},"logos":{"logoLight":null,"logoDark":null}},"statusCode":200},"environments":[{"name":"ETI-Environment","id":"ccd84c46-e859-40f6-be77-828ec604591a","owner":"11528456","values":[{"key":"token","value":"","enabled":true},{"key":"baseUrl","value":"triad60.dev.etisoftware.com","enabled":true,"type":"default"},{"key":"instance","value":"eti-demo-maxicom","enabled":true,"type":"default"}],"published":true}],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/52de83af874f462001674ddb9bdc1bfca2ae7a02c72a76650e1d6907bc6253c0","favicon":"https://etisoftware.com/favicon.ico"},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"},{"label":"ETI-Environment","value":"11528456-ccd84c46-e859-40f6-be77-828ec604591a"}],"canonicalUrl":"https://bls.etisoftware.com/view/metadata/2sA3Bq4B1H"}