Sample code for 30+ languages & platforms
Tcl Requires Chilkat v11.5.0+

Box: Poll User Events and Save a Checkpoint

See more Box Examples

Poll the authenticated user event stream in bounded batches. The local JSON checkpoint keeps processed event IDs and the next stream position. This single-process demonstration logs events; a production worker should commit its business action, deduplication record, and cursor transactionally and prune old IDs.

See the Box API guide for setup and the complete workflow.

Chilkat Tcl Downloads

Tcl

load ./chilkat.dll

set success 0

#  Requires Chilkat 11.5.0 or later and a previously unlocked Chilkat bundle.
#  See Global Unlock Sample.
#  Guide: Box API Guide.

set success 0

#  Supply an unexpired token obtained with one of the Box authentication examples.
#  A token represents a particular user or service account. IDs must be accessible to it.
set curl [new_CkHttpCurl]

CkHttpCurl_SetVar $curl "access_token" "BOX_ACCESS_TOKEN"

set state [new_CkJsonObject]

set files [new_CkFileAccess]

set statePath "qa_data/box_events_state.json"
if {[CkFileAccess_FileExists $files $statePath] == 1} then {
    set success [CkJsonObject_LoadFile $state $statePath]
    if {$success != 1} then {
        puts [CkJsonObject_lastErrorText $state]
        delete_CkHttpCurl $curl
        delete_CkJsonObject $state
        delete_CkFileAccess $files
        exit
    }

    if {[CkJsonObject_HasMember $state "position"] != 1} then {
        puts "Invalid checkpoint: position missing."
        delete_CkHttpCurl $curl
        delete_CkJsonObject $state
        delete_CkFileAccess $files
        exit
    }

} else {
    #  "now" starts a new subscription. It deliberately skips historical events.
    CkJsonObject_UpdateString $state "position" "now"
}

CkHttpCurl_SetVar $curl "position" [CkJsonObject_stringOf $state "position"]
set page [new_CkJsonObject]

set seenPath [new_CkStringBuilder]

set batch 0

set more 1
while {expr [$more == 1]  &&  [$batch < 20]} {
    CkHttpCurl_ClearTargetOutput $curl "*"
    set authorizedCommand1 [new_CkStringBuilder]

    CkStringBuilder_SetString $authorizedCommand1 "curl -X GET \"https://api.box.com/2.0/events?stream_type=all&stream_position={{position}}&limit=100\" -H \"Authorization: Bearer {{access_token}}\""
    set headerReplacement1 [CkStringBuilder_Replace $authorizedCommand1 "{{access_token}}" [CkHttpCurl_getVar $curl "access_token"]]
    set success [CkHttpCurl_DoYourThing $curl [CkStringBuilder_getAsString $authorizedCommand1]]
    if {expr [$success != 1]  ||  [[CkHttpCurl_get_StatusCode $curl] != 200]} then {
        puts "Box request failed. HTTP status: [CkHttpCurl_get_StatusCode $curl]"
        puts "Failure reason: [CkHttpCurl_get_FailReason $curl]"
        puts [CkHttpCurl_responseBodyStr $curl]
        delete_CkHttpCurl $curl
        delete_CkJsonObject $state
        delete_CkFileAccess $files
        delete_CkJsonObject $page
        delete_CkStringBuilder $seenPath
        delete_CkStringBuilder $authorizedCommand1
        exit
    }

    set success [CkHttpCurl_GetResponseJson $curl $page]
    if {$success != 1} then {
        puts [CkHttpCurl_lastErrorText $curl]
        delete_CkHttpCurl $curl
        delete_CkJsonObject $state
        delete_CkFileAccess $files
        delete_CkJsonObject $page
        delete_CkStringBuilder $seenPath
        delete_CkStringBuilder $authorizedCommand1
        exit
    }

    set count [CkJsonObject_SizeOfArray $page "entries"]
    set i 0
    while {$i < $count} {
        CkJsonObject_put_I $page $i
        set eventId [CkJsonObject_stringOf $page "entries[i].event_id"]
        if {$eventId == ""} then {
            puts "Event without an event_id; checkpoint not advanced."
            delete_CkHttpCurl $curl
            delete_CkJsonObject $state
            delete_CkFileAccess $files
            delete_CkJsonObject $page
            delete_CkStringBuilder $seenPath
            delete_CkStringBuilder $authorizedCommand1
            exit
        }

        CkStringBuilder_Clear $seenPath
        CkStringBuilder_Append $seenPath "seen."
        CkStringBuilder_Append $seenPath $eventId
        if {[CkJsonObject_HasMember $state [CkStringBuilder_getAsString $seenPath]] == 0} then {
            #  Replace this logging with your idempotent business operation.
            #  Record success only after that operation succeeds.
            puts "$eventId [CkJsonObject_stringOf $page {entries[i].event_type}] [CkJsonObject_stringOf $page {entries[i].source.id}]"
            CkJsonObject_UpdateBool $state [CkStringBuilder_getAsString $seenPath] 1
        }

        set i [expr $i + 1]
    }
    set nextPosition [CkJsonObject_stringOf $page "next_stream_position"]
    if {$nextPosition == ""} then {
        puts "Missing next_stream_position; checkpoint not advanced."
        delete_CkHttpCurl $curl
        delete_CkJsonObject $state
        delete_CkFileAccess $files
        delete_CkJsonObject $page
        delete_CkStringBuilder $seenPath
        delete_CkStringBuilder $authorizedCommand1
        exit
    }

    set more [expr [$count > 0]  &&  [$nextPosition != [CkHttpCurl_getVar $curl "position"]]]
    CkJsonObject_UpdateString $state "position" $nextPosition
    set success [CkJsonObject_WriteFile $state $statePath]
    if {$success != 1} then {
        puts [CkJsonObject_lastErrorText $state]
        delete_CkHttpCurl $curl
        delete_CkJsonObject $state
        delete_CkFileAccess $files
        delete_CkJsonObject $page
        delete_CkStringBuilder $seenPath
        delete_CkStringBuilder $authorizedCommand1
        exit
    }

    CkHttpCurl_SetVar $curl "position" $nextPosition
    set batch [expr $batch + 1]
}
puts "Checkpoint saved. Run again later to continue polling."

delete_CkHttpCurl $curl
delete_CkJsonObject $state
delete_CkFileAccess $files
delete_CkJsonObject $page
delete_CkStringBuilder $seenPath
delete_CkStringBuilder $authorizedCommand1