File Operations
Process inbound Fedwire files and generate outbound ones
Overview
Every Fedwire message is one ISO 20022 envelope in one file. Twisp processes the files you receive one at a time. It writes the messages you send as separate files in one tar.gz archive per generation.
| Operation | GraphQL | What it does |
|---|---|---|
| Upload | Mutation.files.createUpload() | Get a URL to upload an inbound file |
| Process | Mutation.wire.processFile() | Record an inbound message and apply it |
| Generate | Mutation.wire.generateFile() | Write every queued outbound message to one archive |
| Download | Mutation.files.createDownload() | Fetch a generated archive |
| Inspect | Query.wire.file(), Query.wire.message() | Read a file, its messages and their outcome |
Processing Inbound Files
1. Upload
Upload the file exactly as the sponsor bank delivered it, with upload type WIRE and content type application/xml:
mutation UploadWireFile {
files {
createUpload(
input: {
key: "wires/inbound/20261009-000123.xml"
uploadType: WIRE
contentType: "application/xml"
}
) {
uploadURL
uploadHeaders
}
}
}
PUT the file to uploadURL with uploadHeaders.
2. Process
mutation ProcessWireFile {
wire {
processFile(
input: {
configId: "5d1c6a0e-8b7f-4e2a-9c3d-2e1f0a9b8c7d"
fileKey: "wires/inbound/20261009-000123.xml"
}
) {
fileId
status
messages(first: 1) {
nodes {
messageId
kind
msgId
uetr
status
}
}
}
}
}
processFile parses the envelope and records the file and its message, then returns. The message is applied asynchronously:
- A credit transfer (pacs.008, pacs.009) is decided and posted as an inbound wire.
- A payment status (pacs.002), message reject (admi.002) or payment return (pacs.004) is applied to the outbound wire it answers.
- Other messages, such as a camt.056 return request or a Fed broadcast, are recorded with no action.
processFile fails with BAD_REQUEST when the file is not a Fedwire envelope from the Fed: the wrong root element or namespace, an outgoing-to-Fed envelope, no message or more than one, or an amount that is not a whole number of cents.
Processing Twice
Processing is idempotent:
- Processing the same file key again returns the earlier file and changes nothing.
- A message already recorded under another key is recorded as a new file with no messages and a
detailnaming the earlier message. It is not applied again. - A credit transfer whose UETR an earlier wire already has is recorded as
IGNORED.
Following a Message
Read the message to see its outcome:
query WireMessage {
wire {
message(messageId: "0b9e44a2-6d0c-5b3e-9f1a-2c3d4e5f6a7b") {
kind
status
detail
executionId
}
}
}
| Message status | Meaning |
|---|---|
RECEIVED | Recorded; not yet applied |
PROCESSED | Applied: posted, or applied to its outbound wire. detail says how, such as settle or returned AC03. |
IGNORED | Recorded with no action: a duplicate, a reply for a wire that no longer accepts it, or a message type Twisp does not act on |
ERROR | Could not be applied. detail gives the reason. |
GENERATED | An outbound message written to a generation's archive |
executionId is the workflow execution the message belongs to. For an inbound credit transfer it is the WIRE_INBOUND execution, whose id equals the message id.
Generating Outbound Files
Wires you send (WIRE_OUTBOUND executions in CREATE) and inbound wires you return wait in queues. generateFile writes them to one tar.gz archive at the fileKey you give:
mutation GenerateWireFile {
wire {
generateFile(
input: {
configId: "5d1c6a0e-8b7f-4e2a-9c3d-2e1f0a9b8c7d"
fileKey: "wires/outbound/2026-10-09T15-00.tar.gz"
}
) {
generated
file {
fileId
fileKey
messageCount
totalAmount
messages(first: 100) {
edges {
node {
kind
msgId
uetr
}
}
}
}
}
}
}
For each queued item, generateFile:
- Gives the message the next IMAD.
- Builds a pacs.008 for an outbound wire, or a pacs.004 for a returned inbound wire, and checks it against the Fed usage guidelines.
- Adds it to the archive as its own file.
- Moves an outbound wire to
SUBMIT.
Wires cancelled or rejected while queued are skipped. When nothing is queued, generated is false and no archive is written.
generateFile is idempotent by fileKey. If a key was already generated for the configuration, generateFile returns that generation and writes nothing. Use a new key for each generation, such as one with the time in it. If a call fails, or you do not get its response, call again with the same key: you get the generation if it was written, or a new one if it was not.
Archive Layout
The archive holds one file per message, in IMAD order:
000001_customer_credit_transfer_<uetr>.xml
000002_customer_credit_transfer_<uetr>.xml
000003_payment_return_<uetr>.xml
Each file is one Fedwire envelope. Forward each one to the sponsor bank as its own file. The number at the start is the message's position plus one. The generation's messages, with their IMADs, UETRs and amounts, are also on Query.wire.file().
The archive is complete when generateFile returns. If a generation fails, its wires stay queued, and the next generateFile writes them with the same IMADs. A failed generation can leave an archive at its key. Do not forward it: the archive at a key is valid only after generateFile returns that key with generated: true. A retry with the key replaces the archive.
Downloading
Get a presigned download URL for the archive with Mutation.files.createDownload():
mutation DownloadWireFile {
files {
createDownload(key: "wires/outbound/2026-10-09T15-00.tar.gz") {
downloadURL
downloadURLExpiration
}
}
}
File Statuses
| Status | Meaning |
|---|---|
PROCESSING | An inbound file whose message is being applied |
PROCESSED | An inbound file whose message has an outcome |
ERROR | An inbound file whose message could not be applied |
GENERATED | An outbound generation whose archive is written |