forio Toggle navigation

Run API

The Run API is a RESTful API that collects common tasks around creating and updating runs, variables, and data in one endpoint.

It provides largely the same functionality as the lower-level Data API and Model APIs, but organized around particular user interactions with the project ("runs").

The Run API supports the following methods:

The Run API operates on the following resources / data structures:

POST

You can use the POST request to create new runs and to call model operations on existing runs.

Create a New Run


Method: POST

URI:

  • /v2/run/{account id}/{project id}

Headers:

Body: JSON object with the required fields:

  • model: The primary model file.
  • For a complete list of fields, see details on the run record.

Return Status:

  • 200: Successfully created run
  • 400: Invalid request (examples: missing required field; invalid field added; account, project, or model file not found)
  • 500: Server error while starting the run (example: syntax errors in the model)

Return Body:

The new run record (JSON object) just created.


Example:

curl -X POST \
    'https://api.forio.com/v2/run/acme/supply-chain-game' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \
    --data '{"model": "model.py"}'

Example Response:

{
    "morphology": "MANY",
    "id": "000001539d43a51db44159ddb1a354e484df",
    "user": {
        "firstName": "j",
        "lastName": "smith",
        "id": "233ef9a8-af8e-41ad-84ba-9a21097f6f55",
        "userName": "jsmith@acme.com",
        "email": "jsmith@acme.com"
    },
    "model": "model.py",
    "scope": null,
    "ephemeral": null,
    "created": "2016-03-22T17:17:46.092Z",
    "initialized": true,
    "active": true,
    "project": "supply-chain-game",
    "lastModified": "2016-03-22T17:17:46.092Z",
    "account": "acme",
    "files": null,
    "variables": null,
    "saved": false,
    "trashed": false
}

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).
  • See the error record for more information on how to interpret possible errors.

Call an Operation for One Run


Method: POST

URI:

  • /v2/run/{account id}/{project id}/{run id}/operations/{name of operation}

Headers:

Body: JSON object with the required fields:

  • arguments: An array of the arguments for the operation.
  • For a complete list of fields, see details on the operation record.

Return Status:

  • 200: Successfully called operation
  • 400: Invalid request (examples: unrecognized field in request body; operation not defined in model)

Return Body:

JSON object with the name of the operation, the arguments passed in, and the result (return value) of the operation, if any. For a complete list of fields, see the operation record.


Example:

curl -X POST \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001539d43a51db44159ddb1a354e48506/operations/estimate' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \
    --data '{"arguments": [ "input string", 25, true ]}'

Example Response:

{
    "name": "estimate",
    "arguments": [ "input string", 25, true ],
    "result": 20.44
}

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).
  • General errors with API usage, such as making a call without appropriate permissions, simply return the appropriate HTTP status code. Specific errors because of a problem with the model operation itself return a complete error record.

PATCH

You can use the PATCH request to update the values of run data or model variables associated with the run.

Important: You can update run data at any time. However:

  • You can only update model variables (that is, variables associated with the run) if the run is in memory. Runs are automatically replayed (and so brought back into memory) when you PATCH a variable.
  • For Vensim, you can only update model variables in the current time step.
  • In general, the preferred way to update model variables is to call a method from the model and have the method persist the variables (using the record() statement if your model is in Julia, in Python, or in R).

Update Run Data


Method: PATCH

URI:

  • /v2/run/{account id}/{project id}/{run id}

Headers:

Body: JSON object with fields (names / values) you want to update. For a complete list of fields, see details on the run record.

Return Status:

  • 200: Successfully updated run
  • 400: Invalid request

Return Body:

JSON object with the updated data.


Example:

curl -X PATCH \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001539d43a51db44159ddb1a354e48506' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \
    --data '{"saved": true }'

Example Response:

{
    "saved: true
}

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).
  • Any data that you pass in here is added to the run data. That is, your request body can include both fields that are already part of the run record, and fields that are not initially part of the run record, e.g. {"saved": true, "scenario": "A", "level": "basic"}.

Update Run Data for One or More Runs

For the special case of the run record fields saved, closed, and trashed, you can update these fields for multiple runs at once.


Method: PATCH

URI:

  • /v2/run/{account id}/{project id}/{filter}

Headers:

Body: JSON object with fields (names / values) you want to update. Available fields include saved, closed, and trashed.

Return Status:

  • 204: Successfully updated runs
  • 400: Invalid request

Return Body:

  • None

Example:

curl -X PATCH \
    'https://api.forio.com/v2/run/acme/supply-chain-game/;saved=false' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \
    --data '{"saved": true }'

Example Response:

For a successful request, no response is returned.

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).
  • All runs matching the filter are updated.
  • Only the fields saved, closed, and trashed can be updated using this bulk update format.

Update Variables for One Run


Method: PATCH

URI:

  • /v2/run/{account id}/{project id}/{run id}/variables

Headers:

Body: JSON object whose fields are variable names and whose values are the updated variable values, e.g. { "fieldName1": "fieldValue1", "fieldName2": "fieldValue2"}.

Return Status:

  • 200: Successful response, variables updated
  • 409: Invalid request, conflict (examples: variable not defined in model; value provided for update is not compatible with variable type)

Return Body:

A JSON array with the variables updated and their current (newly updated) values.


Example:

curl -X PATCH \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001539d43a51db44159ddb1a354e48506/variables' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \
    --data '{"sample_int": 16, "sample_dict[\"day\"]": "tuesday", "sample_array[1]": 300}'

Example Response:

{
    "sample_int": 16,
    "sample_dict[\"day\"]": "tuesday",
    "sample_array[1]": 304
}

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).
  • The variable names may include hard-coded array indices (name[index]) or object fields (name.field) if desired. Array indices are modeling-language dependent.
  • If the variable is in a separate scope, that scope must be included in the variable name in the URI (myModule.sample_string).
  • The body of the requst must be valid JSON. Therefore, for boolean variables in Python models,
    • You can use boolean values directly: { "sample_bool": true }.
    • You can use the Python convention for boolean values only if you pass the string value: { "sample_bool": "True" }.
  • For arrayed variables in Vensim models, by default the time step is the last index (name[index, time step]). Use the model context file to specify whether the time step is the first or last index. (Note that for arrayed constants in Vensim models, there is no time index.)
  • For Vensim "LOOKUP" variables, the Epicenter format is an array. Each array element is itself an array with two elements: the x-y pairs from the original Vensim variable. When using the PATCH method, update the entire Epicenter representation (replace all values); you can change the number of ordered pairs if desired. For example:
    • Original Vensim LOOKUP representation: [(2000,0)-(2010,2)],(2000,1),(2005,.5),(2010,1)
    • Epicenter representation: [ [2000,1], [2005,.5], [2010,1] ]
  • For Vensim models, variables designated as "CONSTANT" or "LOOKUP" variables can only be changed at the start of the simulation run (before the Vensim startGame operation is called).
  • For additional examples of working with complex variables (e.g. multidimensional arrays, lists, dicts, etc.), see the specific language pages:
  • Alternatively, you can include the "variables" specifically in the request body, rather than the URI. For example:

      curl -X PATCH \
          'https://api.forio.com/v2/run/acme/supply-chain-game/3575dd84-272f-4ffe-ad64-46d1d621da0a' \
          --header 'Content-Type:application/json' \
          --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \
          --data '{"variables": {"sample_int": 20, "sample_string": "hello again"}}'
    

    Then the response is:

      {
          "variables": { 
              "sample_int": 20,
              "sample_string": "hello again"
          }
      }
    
  • See the error record for more information on how to interpret possible errors.

GET

Retrieve One Run


Method: GET

URI:

  • /v2/run/{account id}/{project id}/{run id}

Headers:

Body: None

Return Status:

  • 200: Successful response
  • 400: Invalid request

Return Body:

The run record (JSON object) for this run.


Example:

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001533965543534cd21a606beb8feb9d6' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response:

{
    "morphology": "MANY",
    "id": "000001539d43a51db44159ddb1a354e484df",
    "user": {
        "firstName": "j",
        "lastName": "smith",
        "id": "233ef9a8-af8e-41ad-84ba-9a21097f6f55",
        "userName": "jsmith@acme.com"
    },
    "model": "model.py",
    "scope": null,
    "created": "2016-03-22T17:17:46.092Z",
    "initialized": true,
    "active": true,
    "project": "supply-chain-game",
    "lastModified": "2016-03-22T17:17:46.092Z",
    "account": "acme",
    "files": null,
    "saved": false,
    "trashed": false
}

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).
  • The run is retrieved from memory if available. If the run is not in memory, it is retrieved from the database.

Retrieve One or More Runs

When you retrieve runs using filters and sorts, all runs are retrieved from the database.


Method: GET

URI:

  • /v2/run/{account id}/{project id}/{filter, sort}

URI Optional Parameters:

  • The {filter} is a set of matrix parameters, and takes the format: ;field1=value1;field2=value2.

    Fields available for filtering include: id, model, saved, scope.worldId.

  • The {sort} is a query parameter indicating the field by which to sort the runs before they are returned, and a second query parameter with the direction of the sort. This takes the format: ?sort=field1&direction=dir.

    Fields available for sorting include: model, created, lastModified. The default is lastModified.

    The direction can be asc (ascending) or desc (descending). The default is desc.

  • See more information on the run record for a complete description of these fields.

Headers:

  • Authorization: Bearer{access token}

  • (Optional) Range: records {i}-{j}

    • Depending on your filter, this request may return many run records. The default page size is 100. You can limit the number of records returned by adding the Range header.

Body: None

Return Status:

  • 200: Successful response
  • 206: Successfully retrieved the partially complete response (for example if you request records 0-20 with the Range header and there are 35 records)
  • 400: Invalid request
  • 416: No records are found in a given search range (for example if you request records 10-15 with the Range header and there are only 8 records)

Return Body:

An array of JSON objects with the run records matching the filter. If you have specified a Range header in the request, or if your request returns more than 100 run records, this may not be all of the run records matching your filter. The array includes only those records indicated in the Content-Range header.


Example:

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/;saved=true?sort=lastModified&direction=asc' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response:

[
    {
        "morphology": "MANY",
        "id": "000001539d43a51db44159ddb1a354e484df",
        "user": {
            "firstName": "j",
            "lastName": "smith",
            "id": "233ef9a8-af8e-41ad-84ba-9a21097f6f55",
            "userName": "jsmith@acme.com"
        },
        "model": "model.py",
        "scope": null,
        "created": "2016-03-22T17:17:46.092Z",
        "initialized": true,
        "active": true,
        "project": "supply-chain-game",
        "lastModified": "2016-03-22T17:17:46.092Z",
        "account": "acme",
        "files": null,
        "saved": true,
        "trashed": false
    },
    ... // other run records matching the filter, if any
        // ordered by lastModified, ascending
]

Notes:

  • All runs are retrieved from the database.
  • If the sort parameter is not specified in the URI, the default sort is by lastModified.
  • If the direction parameter is not specified in the URI, the default direction is desc.

  • Whether or not you add the Range header to your request, the response header contains:

      Content-Range: records i-j/k
    

    where i is the index of the first record returned, j is the index of the last record returned, and k is the total number of records in the result set, or * if that number is computationally infeasible (for instance, because you are querying across multiple projects).

    For example, Content-Range: records 0-9/57 or Content-Range: records 20-29/*.

    If no records are returned, the response body is empty and the Content-Range header in the response is

      Content-Range: records -/0
    

    If you leave off the start index, this is considered an implied start index of 0 rather than an invalid range. The response header includes status code of 200 or 206 depending on the ending index.

    This use of the Range header is part of the RFC 2616 (see details in Section 14.16 and 14.35).

Retrieve Variables as Part of Each Run Record

You can use either the GET request for one run or the GET request for many runs and append ?include= to also return specific model variables in the body of the run record.


Method: GET

URI:

  • /v2/run/{account id}/{project id}/{run id or filter}?include={comma-separated list of variable names}

Headers:

  • Authorization: Bearer{access token}

  • (Optional) Range: records {i}-{j}

    • Depending on your filter, this request may return many records. The default page size is 100. You can limit the number of records returned by adding the Range header.

Body: None

Return Status:

  • 200: Successful response
  • 206: Successfully retrieved the partially complete response (for example if you request records 0-20 with the Range header and there are 35 records)
  • 400: Invalid request
  • 404: Variable not found, when the URI uses the {run id} format and the run is in memory (for example: GET /v2/run/acme/supply-chain-game/1234/?include=badvar)
  • 410: Variable not found (not recorded), when the URI uses the {filter} format, or when the URI uses the {run id} format but the run is not in memory (for example: GET /v2/run/acme/supply-chain-game/;saved=true/?include=badvar)
  • 416: No records are found in a given search range (for example if you request records 10-15 with the Range header and there are only 8 records)

Return Body:

  • If the URI includes a run id directly, the response is the run record, with the named variables also included.
  • If the URI includes a filter (even if the filter is simply ;id=myRunId), the response is an array of such run records.
    • In the case of filtering only, if you have specified a Range header in the request, or if your request returns more than 100 run records, the response may not be all of the run records matching your filter. The response includes only those records indicated in the Content-Range header.
  • See the run record for more information on specific fields returned.

Example (single run id):

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001539d43a51db44159ddb1a354e48542?include=sample_dict,sample_int,sample_array[2]' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response (single run id):

{
    "morphology": "MANY",
    "id": "000001539d43a51db44159ddb1a354e48542",
    "user": {
        "firstName": "j",
        "lastName": "smith",
        "id": "233ef9a8-af8e-41ad-84ba-9a21097f6f55",
        "userName": "jsmith@acme.com"
    },
    "model": "model.py",
    "variables": {
        "sample_array[2]": 6,
        "sample_int": 16,
        "sample_dict": {
            "day": "tuesday",
            "month": 2
        }
    },
    "scope": null,
    "created": "2016-03-22T18:00:14.059Z",
    "initialized": true,
    "active": false,
    "project": "supply-chain-game",
    "lastModified": "2016-03-22T18:00:14.059Z",
    "account": "acme",
    "files": null,
    "saved": true,
    "trashed": false
}

Example (filter):

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/;saved=true?include=sample_dict,sample_int,sample_array' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response (filter):

[
    {
        "morphology": "MANY",
        "id": "000001539d43a51db44159ddb1a354e48542",
        "user": {
            "firstName": "j",
            "lastName": "smith",
            "id": "233ef9a8-af8e-41ad-84ba-9a21097f6f55",
            "userName": "jsmith@acme.com"
        },
        "model": "model.py",
        "variables": {
            "sample_array": [
                2,
                4,
                6,
                8
            ],
            "sample_int": 16,
            "sample_dict": {
                "day": "tuesday",
                "month": 2
            }
        },
        "scope": null,
        "created": "2016-03-22T18:00:14.059Z",
        "initialized": true,
        "active": false,
        "project": "supply-chain-game",
        "lastModified": "2016-03-22T18:00:14.059Z",
        "account": "acme",
        "files": null,
        "saved": true,
        "trashed": false
    },
    ... // other run records matching the filter, if any
]

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).

  • When using a single run id in the URI,

    • The run is retrieved from memory if available. If the run is not in memory, it is retrieved from the database.
    • If the run is in memory, the variables in the response includes all (requested) variables. However, if the run is only in the database, the variables only includes (requested) variables that have been explicitly saved. (Variables are saved to the database using Epicenter.record() or similar, see Writing your model.)
    • The variable name may include hard-coded array indices (name[index]) or object fields (name.field) if desired. Array indices are modeling-language dependent.
  • When using a filter in the URI,

    • When reading from many runs, all information is pulled from the database, regardless of whether the run is also currently in memory. As a consequence, only variables that have been explicitly saved to the database are available for query using this method. (Variables are saved to the database using Epicenter.record() or similar, see Writing your model.)
    • The variable name may NOT include hard-coded array indices (name[index]) or object fields (name.field); only the complete variable is available when you are reading from many runs.
  • There are three different variations that you can use to specify a particular variable name in the URI. The following expressions all add sample_int to the variables in the response body:

    • ?include=sample_int
    • ?include=variables.sample_int
    • ?include=.sample_int
  • If the variable is in a separate scope, that scope must be included in the variable name in the URI (myModule.sample_string).

  • The filter can itself refer to model variables, for example

      curl -G \
          'https://api.forio.com/v2/run/acme/supply-chain-game/;variables=sample_int=12' \
          --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \
    

    Note that as with other filters, when using a filter in the URI, all information is pulled from the database, regardless of whether the run is also currently in memory. In particular, only runs in which the sample_int variable was saved to the database using Epicenter.record() or similar, with the value of 12, are returned.

    The model variable in the filter is included in the returned run record:

      [
          {
              "morphology": "MANY",
              "id": "000001539d43a51db44159ddb1a354e48542",
              "user": {
                  "firstName": "j",
                  "lastName": "smith",
                  "id": "233ef9a8-af8e-41ad-84ba-9a21097f6f55",
                  "userName": "jsmith@acme.com"
              },
              "model": "model.py",
              "variables": {
                  "sample_int": 12
              },
              "scope": null,
              "created": "2016-03-22T18:00:14.059Z",
              "initialized": true,
              "active": false,
              "project": "supply-chain-game",
              "lastModified": "2016-03-22T18:00:14.059Z",
              "account": "acme",
              "files": null,
              "saved": true,
              "trashed": false
          },
          ... // other run records matching the filter, if any
      ]
    

Retrieve Only Variables

You can use either the GET request for one run or the GET request for many runs and append /variables/?include= to return an object with specific model variables request (or an array of such objects).


Method: GET

URI:

  • /v2/run/{account id}/{project id}/{run id or filter}/variables/?include={comma-separated list of variable names}

Headers:

  • Authorization: Bearer{access token}

  • (Optional) Range: records {i}-{j}

    • Depending on your filter, this request may return many records. The default page size is 100. You can limit the number of records returned by adding the Range header.

Body: None

Return Status:

  • 200: Successful response
  • 206: Successfully retrieved the partially complete response (for example if you request records 0-20 with the Range header and there are 35 records)
  • 400: Invalid request
  • 404: Variable not found, when the URI uses the {run id} format and the run is in memory (for example: GET /v2/run/acme/supply-chain-game/1234/variables?include=badvar)
  • 410: Variable not found (not recorded), when the URI uses the {filter} format, or when the URI uses the {run id} format but the run is not in memory (for example: GET /v2/run/acme/supply-chain-game/;saved=true/variables?include=badvar)
  • 416: No records are found in a given search range (for example if you request records 10-15 with the Range header and there are only 8 records)

Return Body:

  • If the URI includes a run id directly, the response is an object whose fields are the requested variables.
  • If the URI includes a filter (even if the filter is simply ;id=myRunId), the response is an array of such objects, one object for each run.
    • In the case of filtering only, if you have specified a Range header in the request, or if your request returns more than 100 runs, this array may not contain objects for all runs matching your filter. The array includes only objects for those runs indicated in the Content-Range header.

Example (single run id):

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001539d43a51db44159ddb1a354e48542/variables/?include=sample_dict,sample_int,sample_array[2]' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response (single run id):

{
    "sample_array[2]": 6,
    "sample_int": 16,
    "sample_dict": {
        "day": "tuesday",
        "month": 2
    }
}

Example (filter):

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/;saved=true/variables/?include=sample_dict,sample_int,sample_array' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response (filter):

[
    {
        "sample_array": [
            2,
            4,
            6,
            8
        ],
        "sample_int": 10,
        "sample_dict": {
            "day": "monday",
            "month": 2
        }
    },
    ... // other run records matching the filter, if any
]

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).

  • When using a single run id in the URI,

    • The run is retrieved from memory if available. If the run is not in memory, it is retrieved from the database.
    • If the run is in memory, the object in the response includes all (requested) variables. However, if the run is only in the database, the object only includes (requested) variables that have been explicitly saved. (Variables are saved to the database using Epicenter.record() or similar, see Writing your model.)
    • The variable name may include hard-coded array indices (name[index]) or object fields (name.field) if desired. Array indices are modeling-language dependent.
  • When using a filter in the URI,

    • When reading from many runs, all information is pulled from the database, regardless of whether the run is also currently in memory. As a consequence, only variables that have been explicitly saved to the database are available for query using this method. (Variables are saved to the database using Epicenter.record() or similar, see Writing your model.)
    • The variable name may NOT include hard-coded array indices (name[index]) or object fields (name.field); only the complete variable is available when you are reading from many runs.
  • There are three different variations that you can use to specify a particular variable name in the URI. The following expressions all add sample_int to the object in the response body:

    • /variables/?include=sample_int
    • /variables/?include=variables.sample_int
    • /variables/?include=.sample_int
  • If the variable is in a separate scope, that scope must be included in the variable name in the URI (myModule.sample_string).

Retrieve a Single, Dereferenced Variable Value from One or More Runs

You can use either the GET request for one run or the GET request for many runs and append /variables/varName to return the dereferenced value of that variable (or an array of such values).


Method: GET

URI:

  • /v2/run/{account id}/{project id}/{run id or filter}/variables/{variable name}

Headers:

  • Authorization: Bearer{access token}

  • (Optional) Range: records {i}-{j}

    • Depending on your filter, this request may return many records. The default page size is 100. You can limit the number of records returned by adding the Range header.

Body: None

Return Status:

  • 200: Successful response
  • 206: Successfully retrieved the partially complete response (for example if you request records 0-20 with the Range header and there are 35 records)
  • 400: Invalid request
  • 404: Variable not found, when the URI uses the {run id} format and the run is in memory (for example: GET /v2/run/acme/supply-chain-game/1234/variables/badvar)
  • 416: No records are found in a given search range (for example if you request records 10-15 with the Range header and there are only 8 records)

Return Body:

  • If the URI includes a run id directly, the response is the value of the variable.
  • If the URI includes a filter (even if the filter is simply ;id=myRunId), the response is an array of such values, one value for each run.
    • In the case of filtering only, if you have specified a Range header in the request, or if your request returns more than 100 runs, this array may not contain variable values for all runs matching your filter. The array includes only values for those runs indicated in the Content-Range header.

Example (single run id):

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001539d43a51db44159ddb1a354e48542/variables/sample_int' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response (single run id):

16

Example (filter):

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/;saved=true/variables/sample_int' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Example Response (filter):

[
    16,
    null, // if the variable does not exist or is not saved for this run
    ... // other variable values for runs matching the filter, if any
]

Notes:

  • In the URI, the Account ID is the Team ID (for team projects) or User ID (for personal projects).

  • When using a single run id in the URI,

    • The run is retrieved from memory if available. If the run is not in memory, it is retrieved from the database. In this case, the requested variable must have been explicitly saved. (Variables are saved to the database using Epicenter.record() or similar, see Writing your model.)
    • The variable name may include hard-coded array indices (name[index]) or object fields (name.field) if desired. Array indices are modeling-language dependent.
    • If the variable does not exist or is not saved, there is an error (404).
  • When using a filter in the URI,

    • When reading from many runs, all information is pulled from the database, regardless of whether the run is also currently in memory. As a consequence, only variables that have been explicitly saved to the database are available for query using this method. (Variables are saved to the database using Epicenter.record() or similar, see Writing your model.)
    • The variable name may NOT include hard-coded array indices (name[index]) or object fields (name.field); only the complete variable is available when you are reading from many runs.
    • If the variable requested does not exist or is not saved for one of the runs, the corresponding entry in the resulting array is null.
  • If the variable is in a separate scope, that scope must be included in the variable name in the URI (myModule.sample_string).

Data Structure

The Run API operates on the Run, Operation, and Error record data structures. It also returns data structures containing variable names and values.

Run Record

The Run record describes the run.

Field Required? Description
id yes The identifier of the run. Always included in a response; should not be included as part of a POST request to create a run.
account yes The Account ID of the account for which the run is created. This is the Team ID (for team projects) or User ID (for personal projects). Required when creating a run and always included in a response.
project yes The Project ID of the project for which the run is created. Required when creating a run and always included in a response.
model yes The name of the primary model file. This is the one file in the project where model execution should begin, and that explicitly exposes variables and operations. Required when creating a run and always included in a response.
user no Information about the user that created the run, including id, userName, firstName, lastName. If the access token used to create the run was a user access token, the user information is included; if it was a project access token, the user information is null.
scope no A description of where the run is available. Typically you use this if your project includes end users and groups; for example, the scope should always be passed in if this run belongs to an end user of your project.
  • group: The name of any local group in the project.
  • worldId: For multiplayer games, the identifier of the world in which this run was created.
  • trackingKey: A string identifier, so you can easily group runs together. This is most common when you have a public project, but want to track all runs associated with a particular user.
created yes When the run record was created, in ISO 8601 format. Always included in a response; should not be included as part of creating a run.
lastModified yes When the run record was most recently updated, in ISO 8601 format. Always included in a response; should not be included as part of creating a run.
files no Only relevant for Vensim models. If you are using a Vensim model and you have additional data to pass in to your model, pass a files object that includes name : value pairs. (Note that you'll also need to add this same files object to your Vensim Model Context file.) Each name can be any designation you like. Each value is name of the file to load. This file must be in your project's Model folder. For example: "files": {"data": "myData.xlsx"} or "files": {"file1": "myFirstFile.xlsx", "file2": "mySecondFile.xlsx"}. See a complete example in How To: Use External Data in Vensim.
active yes This field is true if the run is currently in memory, and false if the run is only in the database. (See more on run persistence.) Always included in a response; should not be included as part of creating a run.
active no A read-only flag in the run record. It is true when the run is in memory and false when the run is only in the databased. See additional information on run persistence.
saved no A flag in the run record, set to false when the run is created. You can set this field as needed; its value is not tied to run creation or run persistence.
trashed no A flag in the run record, set to false when the run is created. You can set this field as needed; its value is not tied to run creation or run persistence.
closed no A flag in the run record, set to false when the run is created. You can set this field as needed; its value is not tied to run creation or run persistence.
initialized no A flag in the run record, set to true when the run is created. You can set this field as needed; its value is not tied to run creation or run persistence.
morphology no Read only and internal use only.


Example Run Record

An example Run record returned as a response to a creating a run:

{
    "user": {
        "id": "d730a1b5-5451-4627-b16d-f3a7ed63f039",
        "userName": "jsmith@acme.com",
        "firstName": "j",
        "lastName": "smith"
    },
    "files": null,
    "morphology": "MANY",
    "created": "2016-03-07T21:29:04.244Z",
    "lastModified": "2016-03-07T21:29:04.244Z",
    "id": "000001533965543534cd21a606beb8feb9c7",
    "account": "acme",
    "project": "supply-chain-game",
    "model": "model.py",
    "saved": false,
    "trashed": false,
    "active": true,
    "initialized": true,
    "scope": null
}

Operation Record

The POST request to call an operation takes an operation record with the arguments of the operation. It returns the operation record with details on the operation called, including name, arguments, and result.

Field Required? Description
name no The name of the model operation called. This is required in the URI of the POST request but is not included in the request body. It is always present in the response.
arguments no An array of arguments to the operation called. If the operation does not take any arguments, you can either omit the arguments field, or pass an empty array. It is present in the response only if arguments were passed in to the operation. Each argument can be of any type (including JSON objects), as long as there is a corresponding operation in the model.
result no The return value from the operation. This is not included in the POST request; it may be present in the response. If there is no return value, this field is not included. Note that the return value may or may not also be stored in a model variable, depending on the operation.


Example Operation Record

An example operation record returned as a response to a POST request:

{
    "name": "runmodel",
    "arguments": [ "input string", 25, true ],
    "result": 20.44
}

Error Record

There are many reasons why the Run API may return an error. These are listed briefly under the specific requests, above. This section describes the error records returned.

Error on Run Creation: 400 Invalid Request

When the POST request cannot be completed because of a problem with the request body, the response is a 400 error.

Field Required? Description
message yes A summary of how the error occurred. This will typically contain enough information to allow you to correct the error.
information.runId yes The potential run id of the run. Note that this run is not accessible, because it was not successfully created.
information.timestamp yes When the error occured.
information.code yes Kind of error, if available, for example MODEL_NOT_FOUND.
information.context yes An object with additional information about where or how the error occured. Fields within this object vary depending on the request. For example, modelFile (the name of the requested model file.)
type yes The kind of underlying error, based on the language in which this part of Epicenter was implmented. This can be safely ignored.


Example: 400 Error Record

{
  "information": {
    "code": "MODEL_NOT_FOUND",
    "context": {
      "modelFile": "othermodel.py"
    },
    "runId": "0000015b92c5fcbf3146463104821549b704",
    "timestamp": "2017-04-24T19:05:56"
  },
  "type": "com.forio.epicenter.grid.transformer.ModelNotFoundException",
  "message": "Could not locate model(/var/epicenter-files/projects/acme/supply_chain_game/model/othermodel.py)"
}

Error on Run Creation: 500 Server Error

When the POST request cannot be completed because of a problem with the model, the response is a 500 error.

Field Required? Description
message yes A summary of how the error occurred. This will typically contain enough information to allow you to correct the error.
information.runKey yes The potential run id of the run. Note that this run is not accessible, because it was not successfully created.
information.code yes Kind of error, if available, for example MODEL_INITIATION.
type yes The kind of underlying error, based on the language in which this part of Epicenter was implmented. This can be safely ignored.
trace yes The stack trace of the error, if available.


Example: 500 Error Record

{
  "information": {
    "runKey": "0000015b92c5fcbf3146463104821549b6f9",
    "code": "MODEL_INITIATION"
  },
  "type": "python",
  "message": "SyntaxError: invalid syntax (badmodel.py, line 6)",
  "trace": [
    {
      "type": "python",
      "function": "load_model",
      "file": "/usr/local/lib/python2.7/dist-packages/epicenter-py2.7.egg/epicenter/worker/python/python_worker.py",
      "line": 75
    },
    {
      "type": "python",
      "function": "import_module",
      "file": "/usr/lib/python2.7/importlib/__init__.py",
      "line": 37
    }
  ],
}

Error on Run Manipulation: 40x

When a POST request or PATCH request cannot be completed, the response is a 40x error.

Field Required? Description
message yes A summary of how the error occurred. This will typically contain enough information to allow you to correct the error.
information.runId yes The unique identifier of the run.
information.timestamp yes When the error occured.
information.code yes A code name for the type of error encountered. For example, OPERATION_ERROR or VARIABLE_NOT_FOUND.
information.context yes An object with additional information about where or how the error occured. Fields within this object vary depending on the request. Examples include names (array of relevant variable or operation names), arguments (array of arguments to an operation, from the request body), value (updated value for variable, from the request body), etc.
type yes The kind of underlying error, based on the language in which this part of Epicenter was implmented. This can be safely ignored.
trace no Depending on the kind of error, a stack trace may also be included in the error record.


Example Error Record for Run Manipulation

An example error record returned as a response to a POST request to call an operation that caused a model error:

{
  "information": {
    "code": "OPERATION_ERROR",
    "context": {
      "name": "updateTwo",
      "arguments": "[15]"
    },
    "runId": "0000015b92c5fcbf3146463104821549b6ef",
    "timestamp": "2017-04-24T19:00:12"
  },
  "type": "python",
  "message": "TypeError: updateTwo() takes exactly 2 arguments (1 given)",
  "trace": [
    {
      "type": "python",
      "function": "process",
      "file": "/usr/local/lib/python2.7/dist-packages/epicenter-py2.7.egg/epicenter/worker/commands.py",
      "line": 240
    },
    {
      "type": "python",
      "function": "proc_call",
      "file": "/usr/local/lib/python2.7/dist-packages/epicenter-py2.7.egg/epicenter/worker/python/python_worker.py",
      "line": 114
    },
    {
      "type": "python",
      "function": "function_call",
      "file": "/usr/local/lib/python2.7/dist-packages/epicenter-py2.7.egg/epicenter/worker/python/json_codec.py",
      "line": 130
    }
  ]
}

Error on Variable Retrieval: 40x

When the GET request cannot be completed, the response is 404 if the run is in memory, or 410 if it is not.

Field Required? Description
message yes A summary of the error which occurred. This will typically contain enough information to allow you to correct the error.
information.runId yes The id of the run.
information.timestamp yes When the error occurred.
information.code yes Kind of error, for example VARIABLE_NOT_FOUND or UNRECORDED_VARIABLE.
information.context.names yes An array of the names of the variables which cannot be retrieved.
type yes The kind of underlying error, based on the language in which this part of Epicenter was implmented. This can be safely ignored.


Example Error Record for Variable Retrieval

Request:

curl -G \
    'https://api.forio.com/v2/run/acme/supply-chain-game/000001539d43a51db44159ddb1a354e48542/variables/?include=goodvar,badvar,otherbadvar' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

Response:

{
  "information": {
    "code": "VARIABLE_NOT_FOUND",
    "context": {
      "names": [
        "badvar",
        "otherbadvar"
      ]
    },
    "runId": "0000015bf8d4bcee880df6b868d23eb3f7f2",
    "timestamp": "2017-05-11T22:10:06"
  },
  "type": "python",
  "message": "NameError: name 'badvar' is not defined"
}