Finance Operations
Reconcile identity-linked payment reversals — Asana task draft
Conserve positive integer minor-unit principal using exact payment references, posted-state rules and explicit currency identity. Supplied facts only; returns a review packet with no external effects. Prepare an Asana task draft.
Experimental native example · local preview · revision 1
WORKFLOW PREVIEW
See how it works
Use + and − to zoom, F to fit. Press Enter on a node to inspect it.
Private preview. These tested drafts are available for inspection. Adding them to a Vora workspace is still being tested.
What you need
- Authorized supplied job facts matching the original closed input contract.
- Explicit review metadata and target destination fields; no implicit team, channel, list or project selection.
Follow the workflow step by step
Supplied agency job and review metadata
Receive the explicitly supplied manual input packet.
Reconcile identity-linked payment reversals
Conserve positive integer minor-unit principal using exact payment references, posted-state rules and explicit currency identity. Supplied facts only; returns a review packet with no external effects.
- default: Asana task draft
Asana task draft
Conserve positive integer minor-unit principal using exact payment references, posted-state rules and explicit currency identity. Supplied facts only; returns a review packet with no external effects.
Setup and review
- Download the workflow JSON and inspect its jobInput and deliveryMetadata contracts.
- Supply the original agency job input plus explicit review metadata and destination.
- Run in an isolated test and inspect the complete jobResult, target status, holds and escape report before taking an external action.
Inspect every node
Expand a node to inspect its complete settings. Review the runtime and adoption evidence before installing it.
Supplied agency job and review metadata · wfManualTrigger
{
"label": "Supplied agency job and review metadata"
}Reconcile identity-linked payment reversals · code
{
"label": "Reconcile identity-linked payment reversals",
"code": "return (function(context){\n\nfunction fail(reason){throw new Error('input: '+reason);}\nfunction check(schema,value,where){\n if(value===null){if(schema.type==='null'||Array.isArray(schema.type)&&schema.type.includes('null'))return;fail(where+' wrong_type');}\n const type=Array.isArray(schema.type)?schema.type[0]:schema.type;\n if(type==='object'){\n if(!value||typeof value!=='object'||Array.isArray(value))fail(where+' wrong_type');\n for(const name of Object.keys(value))if(!Object.hasOwn(schema.properties,name))fail(where+' unknown_field');\n for(const name of schema.required)if(!Object.hasOwn(value,name))fail(where+' missing_field');\n for(const [name,shape]of Object.entries(schema.properties))check(shape,value[name],where+'.'+name);\n }else if(type==='array'){\n if(!Array.isArray(value)||value.length>schema.maxItems)fail(where+' array_bound');\n for(const item of value)check(schema.items,item,where+'[]');\n }else if(type==='string'){if(typeof value!=='string'||value.length>schema.maxLength)fail(where+' string_bound');}\n else if(type==='integer'){if(!Number.isSafeInteger(value)||value<schema.minimum||value>schema.maximum)fail(where+' integer_bound');}\n else if(type==='boolean'){if(typeof value!=='boolean')fail(where+' wrong_type');}\n else fail(where+' unsupported_type');\n}\nfunction id(value){if(typeof value!=='string'||!value||value.trim()!==value)fail('noncanonical_identity');return value;}\nfunction unique(values,reason){if(new Set(values).size!==values.length)fail(reason);}\nfunction safeAdd(a,b){if(a>Number.MAX_SAFE_INTEGER-b)fail('unsafe_sum');return a+b;}\nfunction day(value){if(!/^\\d{4}-\\d{2}-\\d{2}$/.test(value))fail('invalid_calendar_day');const ms=Date.parse(value+'T00:00:00Z');if(!Number.isFinite(ms)||new Date(ms).toISOString().slice(0,10)!==value)fail('invalid_calendar_day');return ms;}\nfunction refKey(ref){id(ref.assetId);if(ref.version<1)fail('positive_version_required');return JSON.stringify([ref.assetId,ref.version]);}\nfunction refValue(key){const pair=JSON.parse(key);return {assetId:pair[0],version:pair[1]};}\nfunction sortedRefs(keys){return [...keys].sort().map(refValue);}\n\ncheck({\"type\":\"object\",\"properties\":{\"scopeId\":{\"type\":\"string\",\"maxLength\":128},\"currency\":{\"type\":\"string\",\"maxLength\":128},\"inputComplete\":{\"type\":[\"boolean\",\"null\"]},\"events\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"eventId\":{\"type\":\"string\",\"maxLength\":128},\"revision\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kind\":{\"type\":\"string\",\"maxLength\":128},\"state\":{\"type\":\"string\",\"maxLength\":128},\"amountMinor\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"currency\":{\"type\":\"string\",\"maxLength\":128},\"originalPaymentId\":{\"type\":[\"string\",\"null\"],\"maxLength\":128}},\"required\":[\"eventId\",\"revision\",\"kind\",\"state\",\"amountMinor\",\"currency\",\"originalPaymentId\"],\"additionalProperties\":false},\"maxItems\":100}},\"required\":[\"scopeId\",\"currency\",\"inputComplete\",\"events\"],\"additionalProperties\":false},context.input,\"input\");\nconst result=(function paymentReversalConservation(x){\n id(x.scopeId);id(x.currency);\n const groups=new Map();\n for(const row of x.events){\n id(row.eventId);id(row.currency);\n if(row.revision<1)fail('positive_revision_required');\n if(!['payment','reversal'].includes(row.kind))fail('invalid_event_kind');\n if(!['posted','pending'].includes(row.state))fail('invalid_event_state');\n if(row.amountMinor<=0)fail('positive_amount_required');\n if(row.kind==='payment'&&row.originalPaymentId!==null)fail('invalid_payment_reference');\n if(row.kind==='reversal'){if(row.originalPaymentId===null)fail('reversal_reference_required');id(row.originalPaymentId);}\n if(!groups.has(row.eventId))groups.set(row.eventId,[]);groups.get(row.eventId).push(row);\n }\n const resolved=[],identical=[],conflicting=[];\n for(const [eventId,rows]of groups){\n const signatures=new Set(rows.map(r=>JSON.stringify([r.revision,r.kind,r.state,r.amountMinor,r.currency,r.originalPaymentId])));\n if(signatures.size>1){conflicting.push(eventId);continue;}\n if(rows.length>1)identical.push(eventId);resolved.push(rows[0]);\n }\n const paymentsById=new Map(resolved.filter(r=>r.kind==='payment').map(r=>[r.eventId,r]));\n const posted=resolved.filter(r=>r.kind==='payment'&&r.state==='posted'&&r.currency===x.currency);\n const reversals=resolved.filter(r=>r.kind==='reversal'),currencyConflicts=new Set(resolved.filter(r=>r.currency!==x.currency).map(r=>r.eventId)),orphans=[],unposted=[],sums=new Map();\n for(const row of reversals){\n const original=paymentsById.get(row.originalPaymentId);\n if(!original){orphans.push(row.eventId);continue;}\n if(row.currency!==original.currency||row.currency!==x.currency){currencyConflicts.add(row.eventId);continue;}\n if(row.state==='posted'&&original.state!=='posted'){unposted.push(row.eventId);continue;}\n if(row.state==='posted'&&original.state==='posted')sums.set(original.eventId,safeAdd(sums.get(original.eventId)||0,row.amountMinor));\n }\n const over=[],paymentResults=[];let principal=0,totalReversed=0;\n for(const original of posted){\n principal=safeAdd(principal,original.amountMinor);\n const reversed=sums.get(original.eventId)||0;totalReversed=safeAdd(totalReversed,reversed);\n const excess=reversed>original.amountMinor;\n if(excess)over.push(original.eventId);\n const invalidLinkedCurrency=reversals.some(r=>r.originalPaymentId===original.eventId&¤cyConflicts.has(r.eventId));\n paymentResults.push({paymentId:original.eventId,postedPrincipalMinor:original.amountMinor,linkedPostedReversalMinor:reversed,knownNetMinor:excess||invalidLinkedCurrency?null:original.amountMinor-reversed});\n }\n paymentResults.sort((a,b)=>a.paymentId<b.paymentId?-1:a.paymentId>b.paymentId?1:0);\n const conflict=conflicting.length>0||currencyConflicts.size>0||unposted.length>0||over.length>0;\n const status=conflict?'conflict':x.inputComplete!==true||orphans.length?'unknown':'conserved';\n return {status,currency:x.currency,inputComplete:x.inputComplete,postedPrincipalMinor:principal,conservedNetMinor:status==='conserved'?principal-totalReversed:null,payments:paymentResults,pendingEventIds:resolved.filter(r=>r.state==='pending').map(r=>r.eventId).sort(),identicalDuplicateEventIds:identical.sort(),conflictingEventIds:conflicting.sort(),currencyConflictIds:[...currencyConflicts].sort(),orphanReversalIds:orphans.sort(),unpostedReferenceReversalIds:unposted.sort(),overReversedPaymentIds:over.sort()};\n})(context.input);\ncheck({\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\",\"maxLength\":128},\"currency\":{\"type\":\"string\",\"maxLength\":128},\"inputComplete\":{\"type\":[\"boolean\",\"null\"]},\"postedPrincipalMinor\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"conservedNetMinor\":{\"type\":[\"integer\",\"null\"],\"minimum\":0,\"maximum\":9007199254740991},\"payments\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"paymentId\":{\"type\":\"string\",\"maxLength\":128},\"postedPrincipalMinor\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"linkedPostedReversalMinor\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"knownNetMinor\":{\"type\":[\"integer\",\"null\"],\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"paymentId\",\"postedPrincipalMinor\",\"linkedPostedReversalMinor\",\"knownNetMinor\"],\"additionalProperties\":false},\"maxItems\":100},\"pendingEventIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":128},\"maxItems\":100},\"identicalDuplicateEventIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":128},\"maxItems\":100},\"conflictingEventIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":128},\"maxItems\":100},\"currencyConflictIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":128},\"maxItems\":100},\"orphanReversalIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":128},\"maxItems\":100},\"unpostedReferenceReversalIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":128},\"maxItems\":100},\"overReversedPaymentIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":128},\"maxItems\":100}},\"required\":[\"status\",\"currency\",\"inputComplete\",\"postedPrincipalMinor\",\"conservedNetMinor\",\"payments\",\"pendingEventIds\",\"identicalDuplicateEventIds\",\"conflictingEventIds\",\"currencyConflictIds\",\"orphanReversalIds\",\"unpostedReferenceReversalIds\",\"overReversedPaymentIds\"],\"additionalProperties\":false},result,\"output\");\nreturn result;\n})({input:context.input.jobInput});\n",
"outputVar": "jobResult",
"timeout": 3000,
"memoryLimit": 64
}Asana task draft · code
{
"label": "Asana task draft",
"code": "const jobResult=context.jobResult;\nconst delivery=(function(context){\nreturn ((function(originalResult,metadata,destination){\nfunction stableResult(value,depth=0){\n if(depth>64)throw new Error('Result nesting exceeds 64');\n if(value===null||typeof value==='string'||typeof value==='boolean')return JSON.stringify(value);\n if(typeof value==='number'&&Number.isFinite(value))return JSON.stringify(value);\n if(Array.isArray(value)){\n if(Object.keys(value).length!==value.length||Array.from({length:value.length},(_,i)=>i).some(i=>!Object.hasOwn(value,i)))throw new Error('Result contains sparse data');\n return '['+value.map(item=>stableResult(item,depth+1)).join(',')+']';\n }\n if(value&&typeof value==='object')return '{'+Object.keys(value).sort().map(key=>JSON.stringify(key)+':'+stableResult(value[key],depth+1)).join(',')+'}';\n throw new Error('Result is not JSON data');\n}\nfunction utf8Length(text){\n let bytes=0;\n for(let i=0;i<text.length;i++){\n const code=text.charCodeAt(i);\n if(code<128)bytes++;else if(code<2048)bytes+=2;\n else if(code>=55296&&code<=56319&&i+1<text.length&&text.charCodeAt(i+1)>=56320&&text.charCodeAt(i+1)<=57343){bytes+=4;i++;}\n else bytes+=3;\n }\n return bytes;\n}\nfunction chunkText(text,maximum){\n const chunks=[];\n for(let start=0;start<text.length;){let end=Math.min(start+maximum,text.length);const last=text.charCodeAt(end-1),next=text.charCodeAt(end);if(end<text.length&&last>=55296&&last<=56319&&next>=56320&&next<=57343)end--;chunks.push(text.slice(start,end));start=end;}\n return chunks;\n}\nfunction deliveryState(rendererId,originalResult,metadata,destination,destinationKeys){\n const output={rendererId,status:'HELD',originalResult,drafts:[],holds:[],escapeReport:[]};\n const hold=(code,message)=>{output.holds.push({code,message});return {output,json:''};};\n const object=value=>value&&typeof value==='object'&&!Array.isArray(value);\n const exact=(value,keys)=>object(value)&&Object.keys(value).length===keys.length&&keys.every(key=>Object.hasOwn(value,key));\n const keys=['workflowId','canonicalIntentId','title','reviewUrl'],maxima=[128,200,200,1000];\n if(!exact(metadata,keys)||keys.some((key,i)=>typeof metadata[key]!=='string'||!metadata[key].length||metadata[key].length>maxima[i]||/[\\u0000-\\u001f\\u007f]/.test(metadata[key]))||!/^https?:\\/\\/[^\\s<>\"']+$/.test(metadata.reviewUrl))return hold('INVALID_METADATA','Metadata must provide bounded workflowId, canonicalIntentId, title and an HTTP(S) reviewUrl.');\n if(!exact(destination,destinationKeys)||destinationKeys.some(key=>typeof destination[key]!=='string'||!destination[key].length||destination[key].length>200||destination[key]!==destination[key].trim()||/[\\u0000-\\u001f\\u007f]/.test(destination[key])))return hold('INVALID_DESTINATION','Destination fields must be exactly: '+destinationKeys.join(', ')+'.');\n let json;\n try{if(!object(originalResult))throw new Error('Primary result must be a JSON object');json=stableResult(originalResult);}catch(error){return hold('INVALID_RESULT',error.message);}\n if(json.length>32768||utf8Length(json)>65536)return hold('RESULT_BOUND_EXCEEDED','Original result exceeds this renderer profile; full original result is retained.');\n return {output,json};\n}\nfunction escapeHtml(text){return text.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"').replace(/'/g,''');}\nfunction escapeMarkdown(text){return text.replace(/&/g,'&').replace(/[\\\\`*{}\\[\\]()#+\\-.!_|>~<]/g,'\\\\$&');}\nfunction markdownReviewText(metadata,json){\n const runs=json.match(/`+/g)||[],fence='`'.repeat(Math.max(3,...runs.map(run=>run.length+1)));\n return '# '+escapeMarkdown(metadata.title)+'\\n\\nWorkflow: '+escapeMarkdown(metadata.workflowId)+'\\nCanonical job: '+escapeMarkdown(metadata.canonicalIntentId)+'\\nReview: <'+metadata.reviewUrl.replace(/&/g,'&')+'>\\n\\n'+fence+'json\\n'+json+'\\n'+fence+'\\n';\n}\nfunction plainReviewText(metadata,json){return metadata.title+'\\nWorkflow: '+metadata.workflowId+'\\nCanonical job: '+metadata.canonicalIntentId+'\\nReview: '+metadata.reviewUrl+'\\n\\n'+json;}\nfunction finishDraft(state,draft,texts=[],textMaximum=32768,byteMaximum=131072){\n if(texts.some(text=>text.length>textMaximum)||utf8Length(JSON.stringify(draft))>byteMaximum){state.output.holds.push({code:'TARGET_BOUND_EXCEEDED',message:'Draft exceeds the target or Vora profile bound; no draft is emitted and the full original result is retained.'});return state.output;}\n state.output.status='PREPARED';state.output.drafts=[draft];return state.output;\n}\nreturn (function asanaTaskReview(originalResult,metadata,destination){\n const state=deliveryState('asana-task-review',originalResult,metadata,destination,['workspaceId']);if(state.output.holds.length)return state.output;\n const notes=plainReviewText(metadata,state.json);return finishDraft(state,{body:{data:{workspace:destination.workspaceId,name:metadata.title,notes}}},[notes]);\n})(originalResult,metadata,destination);\n}))(context.input.jobResult,context.input.deliveryMetadata.metadata,context.input.deliveryMetadata.destination);\n\n})({input:{jobResult,deliveryMetadata:context.input.deliveryMetadata}});\nreturn {jobResult,delivery};\n",
"outputVar": "result",
"timeout": 3000,
"memoryLimit": 64
}Input and output contracts
Input
{
"type": "object",
"properties": {
"jobInput": {
"type": "object",
"properties": {
"scopeId": {
"type": "string",
"maxLength": 128
},
"currency": {
"type": "string",
"maxLength": 128
},
"inputComplete": {
"type": [
"boolean",
"null"
]
},
"events": {
"type": "array",
"items": {
"type": "object",
"properties": {
"eventId": {
"type": "string",
"maxLength": 128
},
"revision": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"kind": {
"type": "string",
"maxLength": 128
},
"state": {
"type": "string",
"maxLength": 128
},
"amountMinor": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"currency": {
"type": "string",
"maxLength": 128
},
"originalPaymentId": {
"type": [
"string",
"null"
],
"maxLength": 128
}
},
"required": [
"eventId",
"revision",
"kind",
"state",
"amountMinor",
"currency",
"originalPaymentId"
],
"additionalProperties": false
},
"maxItems": 100
}
},
"required": [
"scopeId",
"currency",
"inputComplete",
"events"
],
"additionalProperties": false
},
"deliveryMetadata": {
"type": "object",
"properties": {
"metadata": {
"type": "object",
"properties": {
"workflowId": {
"type": "string",
"maxLength": 128
},
"canonicalIntentId": {
"type": "string",
"maxLength": 200
},
"title": {
"type": "string",
"maxLength": 200
},
"reviewUrl": {
"type": "string",
"maxLength": 1000
}
},
"required": [
"workflowId",
"canonicalIntentId",
"title",
"reviewUrl"
],
"additionalProperties": false
},
"destination": {
"type": "object",
"properties": {
"workspaceId": {
"type": "string",
"maxLength": 200
}
},
"required": [
"workspaceId"
],
"additionalProperties": false
}
},
"required": [
"metadata",
"destination"
],
"additionalProperties": false
}
},
"required": [
"jobInput",
"deliveryMetadata"
],
"additionalProperties": false
}Output
{
"type": "object",
"properties": {
"jobResult": {
"type": "object",
"properties": {
"status": {
"type": "string",
"maxLength": 128
},
"currency": {
"type": "string",
"maxLength": 128
},
"inputComplete": {
"type": [
"boolean",
"null"
]
},
"postedPrincipalMinor": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"conservedNetMinor": {
"type": [
"integer",
"null"
],
"minimum": 0,
"maximum": 9007199254740991
},
"payments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"paymentId": {
"type": "string",
"maxLength": 128
},
"postedPrincipalMinor": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"linkedPostedReversalMinor": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"knownNetMinor": {
"type": [
"integer",
"null"
],
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"paymentId",
"postedPrincipalMinor",
"linkedPostedReversalMinor",
"knownNetMinor"
],
"additionalProperties": false
},
"maxItems": 100
},
"pendingEventIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"identicalDuplicateEventIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"conflictingEventIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"currencyConflictIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"orphanReversalIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"unpostedReferenceReversalIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"overReversedPaymentIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
}
},
"required": [
"status",
"currency",
"inputComplete",
"postedPrincipalMinor",
"conservedNetMinor",
"payments",
"pendingEventIds",
"identicalDuplicateEventIds",
"conflictingEventIds",
"currencyConflictIds",
"orphanReversalIds",
"unpostedReferenceReversalIds",
"overReversedPaymentIds"
],
"additionalProperties": false
},
"delivery": {
"type": "object",
"properties": {
"rendererId": {
"type": "string",
"maxLength": 128,
"enum": [
"asana-task-review"
]
},
"status": {
"type": "string",
"maxLength": 16,
"enum": [
"PREPARED",
"HELD"
]
},
"originalResult": {
"type": "object",
"properties": {
"status": {
"type": "string",
"maxLength": 128
},
"currency": {
"type": "string",
"maxLength": 128
},
"inputComplete": {
"type": [
"boolean",
"null"
]
},
"postedPrincipalMinor": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"conservedNetMinor": {
"type": [
"integer",
"null"
],
"minimum": 0,
"maximum": 9007199254740991
},
"payments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"paymentId": {
"type": "string",
"maxLength": 128
},
"postedPrincipalMinor": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"linkedPostedReversalMinor": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"knownNetMinor": {
"type": [
"integer",
"null"
],
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"paymentId",
"postedPrincipalMinor",
"linkedPostedReversalMinor",
"knownNetMinor"
],
"additionalProperties": false
},
"maxItems": 100
},
"pendingEventIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"identicalDuplicateEventIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"conflictingEventIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"currencyConflictIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"orphanReversalIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"unpostedReferenceReversalIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
},
"overReversedPaymentIds": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128
},
"maxItems": 100
}
},
"required": [
"status",
"currency",
"inputComplete",
"postedPrincipalMinor",
"conservedNetMinor",
"payments",
"pendingEventIds",
"identicalDuplicateEventIds",
"conflictingEventIds",
"currencyConflictIds",
"orphanReversalIds",
"unpostedReferenceReversalIds",
"overReversedPaymentIds"
],
"additionalProperties": false
},
"drafts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"body": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"workspace": {
"type": "string",
"maxLength": 200
},
"name": {
"type": "string",
"maxLength": 200
},
"notes": {
"type": "string",
"maxLength": 32768
}
},
"required": [
"workspace",
"name",
"notes"
],
"additionalProperties": false
}
},
"required": [
"data"
],
"additionalProperties": false
}
},
"required": [
"body"
],
"additionalProperties": false
},
"maxItems": 1,
"minItems": 0
},
"holds": {
"type": "array",
"items": {
"type": "object",
"properties": {
"code": {
"type": "string",
"maxLength": 128
},
"message": {
"type": "string",
"maxLength": 2000
}
},
"required": [
"code",
"message"
],
"additionalProperties": false
},
"maxItems": 10,
"minItems": 0
},
"escapeReport": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": {
"type": "string",
"maxLength": 256
},
"method": {
"type": "string",
"maxLength": 80
},
"originalValue": {
"type": "string",
"maxLength": 32768
},
"escapedValue": {
"type": "string",
"maxLength": 32768
}
},
"required": [
"field",
"method",
"originalValue",
"escapedValue"
],
"additionalProperties": false
},
"maxItems": 1000,
"minItems": 0
}
},
"required": [
"rendererId",
"status",
"originalResult",
"drafts",
"holds",
"escapeReport"
],
"additionalProperties": false
}
},
"required": [
"jobResult",
"delivery"
],
"additionalProperties": false
}Related implementations
Compare implementations for this job · Primary business workflow
Caller workspaceId maps to data.workspace; name and notes carry the title and complete plain-text review. No html_notes, projects, assignees or provider actions are produced. Original result, including original reasons/holds, is preserved without mutation. Missing/extra destination fields or invalid metadata hold the draft. No truncation: overflow returns HELD with an empty drafts array and full original result.
Supplied input: vora-agency-payment-reversal-conservation · 1. Draft/result: asana-task-review · Asana REST API 1.0 tasks.
Mapping and loss rules
- Caller workspaceId maps to data.workspace; name and notes carry the title and complete plain-text review.
- No html_notes, projects, assignees or provider actions are produced.
- Original result, including original reasons/holds, is preserved without mutation.
- Missing/extra destination fields or invalid metadata hold the draft.
- No truncation: overflow returns HELD with an empty drafts array and full original result.
This binding consumes supplied data. It does not establish an authenticated connector or a provider write.
Example input and result
These examples use synthetic data.
Input
{
"jobInput": {
"scopeId": "client-A",
"currency": "USD",
"inputComplete": true,
"events": [
{
"eventId": "pay-A",
"revision": 1,
"kind": "payment",
"state": "posted",
"amountMinor": 1000,
"currency": "USD",
"originalPaymentId": null
},
{
"eventId": "rev-A",
"revision": 1,
"kind": "reversal",
"state": "posted",
"amountMinor": 300,
"currency": "USD",
"originalPaymentId": "pay-A"
}
]
},
"deliveryMetadata": {
"metadata": {
"workflowId": "agency-payment-reversal-conservation",
"canonicalIntentId": "finance.reconcile-linked-payment-reversals",
"title": "Reconcile identity-linked payment reversals",
"reviewUrl": "https://example.test/workflows/payment-reversal-conservation/"
},
"destination": {
"workspaceId": "1234567890"
}
}
}Expected result
{
"jobResult": {
"status": "conserved",
"currency": "USD",
"inputComplete": true,
"postedPrincipalMinor": 1000,
"conservedNetMinor": 700,
"payments": [
{
"paymentId": "pay-A",
"postedPrincipalMinor": 1000,
"linkedPostedReversalMinor": 300,
"knownNetMinor": 700
}
],
"pendingEventIds": [],
"identicalDuplicateEventIds": [],
"conflictingEventIds": [],
"currencyConflictIds": [],
"orphanReversalIds": [],
"unpostedReferenceReversalIds": [],
"overReversedPaymentIds": []
},
"delivery": {
"rendererId": "asana-task-review",
"status": "PREPARED",
"originalResult": {
"status": "conserved",
"currency": "USD",
"inputComplete": true,
"postedPrincipalMinor": 1000,
"conservedNetMinor": 700,
"payments": [
{
"paymentId": "pay-A",
"postedPrincipalMinor": 1000,
"linkedPostedReversalMinor": 300,
"knownNetMinor": 700
}
],
"pendingEventIds": [],
"identicalDuplicateEventIds": [],
"conflictingEventIds": [],
"currencyConflictIds": [],
"orphanReversalIds": [],
"unpostedReferenceReversalIds": [],
"overReversedPaymentIds": []
},
"drafts": [
{
"body": {
"data": {
"workspace": "1234567890",
"name": "Reconcile identity-linked payment reversals",
"notes": "Reconcile identity-linked payment reversals\nWorkflow: agency-payment-reversal-conservation\nCanonical job: finance.reconcile-linked-payment-reversals\nReview: https://example.test/workflows/payment-reversal-conservation/\n\n{\"conflictingEventIds\":[],\"conservedNetMinor\":700,\"currency\":\"USD\",\"currencyConflictIds\":[],\"identicalDuplicateEventIds\":[],\"inputComplete\":true,\"orphanReversalIds\":[],\"overReversedPaymentIds\":[],\"payments\":[{\"knownNetMinor\":700,\"linkedPostedReversalMinor\":300,\"paymentId\":\"pay-A\",\"postedPrincipalMinor\":1000}],\"pendingEventIds\":[],\"postedPrincipalMinor\":1000,\"status\":\"conserved\",\"unpostedReferenceReversalIds\":[]}"
}
}
}
],
"holds": [],
"escapeReport": []
}
}Success means: Conserve positive integer minor-unit principal using exact payment references, posted-state rules and explicit currency identity. Supplied facts only; returns a review packet with no external effects.
When something goes wrong
- Review original business holds and target holds separately. Correct the relevant supplied input and rerun without discarding the original job result.
Limits and costs
- This prepares a target-specific review draft from the original agency decision. The complete original job result and business holds remain visible. A held target produces no draft. No provider request, send, import, issue creation or publication occurs.
- No paid model calls or metered third-party operations are declared in this pure graph. Vora plan availability, compute and quotas still apply; pricing has not been verified by this batch.